mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
[FEATURE] import a layer from canvas into a PG database
This commit is contained in:
parent
7a2a328fdd
commit
00d7498075
@ -60,6 +60,18 @@ class QgsProviderRegistry
|
||||
* @note this method was added in QGIS 1.1
|
||||
*/
|
||||
virtual QString protocolDrivers() const;
|
||||
|
||||
/** allows to import a vector layer using the provider
|
||||
* @note this method was added in QGIS 1.8
|
||||
*/
|
||||
int importVector( QgsVectorLayer* layer,
|
||||
const QString& providerKey,
|
||||
const QString& uri,
|
||||
const QgsCoordinateReferenceSystem *destCRS,
|
||||
bool onlySelected = FALSE,
|
||||
QString *errorMessage /Out/ = 0,
|
||||
bool skipAttributeCreation = FALSE
|
||||
) const;
|
||||
|
||||
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "qgslogger.h"
|
||||
#include "qgsmessageoutput.h"
|
||||
#include "qgsprovidermetadata.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
|
||||
|
||||
// typedefs for provider plugin functions of interest
|
||||
@ -42,6 +43,12 @@ typedef QString directoryDrivers_t();
|
||||
typedef QString protocolDrivers_t();
|
||||
//typedef int dataCapabilities_t();
|
||||
//typedef QgsDataItem * dataItem_t(QString);
|
||||
typedef int importVector_t(QgsVectorLayer* layer,
|
||||
const QString& uri,
|
||||
const QgsCoordinateReferenceSystem *destCRS,
|
||||
bool onlySelected = false,
|
||||
QString *errorMessage = 0,
|
||||
bool skipAttributeCreation = false);
|
||||
|
||||
QgsProviderRegistry *QgsProviderRegistry::_instance = 0;
|
||||
|
||||
@ -468,7 +475,7 @@ void * QgsProviderRegistry::function( QString const & providerKey,
|
||||
return 0;
|
||||
}
|
||||
|
||||
QLibrary *QgsProviderRegistry::providerLibrary( QString const & providerKey )
|
||||
QLibrary *QgsProviderRegistry::providerLibrary( QString const & providerKey ) const
|
||||
{
|
||||
QString lib = library( providerKey );
|
||||
|
||||
@ -531,3 +538,34 @@ QgsProviderRegistry::openVector( QString const & dataSource, QString const & pro
|
||||
return getProvider( providerKey, dataSource );
|
||||
} // QgsProviderRegistry::openVector
|
||||
*/
|
||||
|
||||
|
||||
int QgsProviderRegistry::importVector( QgsVectorLayer* layer,
|
||||
const QString& providerKey,
|
||||
const QString& uri,
|
||||
const QgsCoordinateReferenceSystem *destCRS,
|
||||
bool onlySelected,
|
||||
QString *errorMessage,
|
||||
bool skipAttributeCreation
|
||||
) const
|
||||
{
|
||||
QLibrary *myLib = providerLibrary( providerKey );
|
||||
if ( !myLib )
|
||||
{
|
||||
if ( errorMessage )
|
||||
*errorMessage = QObject::tr( "unable to load %1 provider" ).arg( providerKey );
|
||||
return -1;
|
||||
}
|
||||
|
||||
importVector_t * pImport = ( importVector_t * ) cast_to_fptr( myLib->resolve( "importVector" ) );
|
||||
if ( !pImport )
|
||||
{
|
||||
delete myLib;
|
||||
if ( errorMessage )
|
||||
*errorMessage = QObject::tr( "provider %1 has no importVector feature" ).arg( providerKey );
|
||||
return -2;
|
||||
}
|
||||
|
||||
delete myLib;
|
||||
return pImport( layer, uri, destCRS, onlySelected, errorMessage, skipAttributeCreation );
|
||||
}
|
||||
|
@ -28,7 +28,8 @@
|
||||
|
||||
class QgsDataProvider;
|
||||
class QgsProviderMetadata;
|
||||
|
||||
class QgsVectorLayer;
|
||||
class QgsCoordinateReferenceSystem;
|
||||
|
||||
|
||||
/** \ingroup core
|
||||
@ -78,7 +79,7 @@ class CORE_EXPORT QgsProviderRegistry
|
||||
void *function( const QString & providerKey,
|
||||
const QString & functionName );
|
||||
|
||||
QLibrary *providerLibrary( const QString & providerKey );
|
||||
QLibrary *providerLibrary( const QString & providerKey ) const;
|
||||
|
||||
/** Return list of available providers by their keys */
|
||||
QStringList providerList() const;
|
||||
@ -139,6 +140,16 @@ class CORE_EXPORT QgsProviderRegistry
|
||||
/** type for data provider metadata associative container */
|
||||
typedef std::map<QString, QgsProviderMetadata*> Providers;
|
||||
|
||||
/** allows to import a vector layer using the provider */
|
||||
int importVector( QgsVectorLayer* layer,
|
||||
const QString& providerKey,
|
||||
const QString& uri,
|
||||
const QgsCoordinateReferenceSystem *destCRS,
|
||||
bool onlySelected = false,
|
||||
QString *errorMessage = 0,
|
||||
bool skipAttributeCreation = false
|
||||
) const;
|
||||
|
||||
private:
|
||||
|
||||
/** ctor private since instance() creates it */
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <qgis.h>
|
||||
#include <qgsapplication.h>
|
||||
#include <qgsvectorlayer.h>
|
||||
#include <qgsfeature.h>
|
||||
#include <qgsfield.h>
|
||||
#include <qgsgeometry.h>
|
||||
@ -54,6 +55,493 @@ QMap<QString, QgsPostgresProvider::Conn *> QgsPostgresProvider::Conn::connection
|
||||
QMap<QString, QString> QgsPostgresProvider::Conn::passwordCache;
|
||||
int QgsPostgresProvider::providerIds = 0;
|
||||
|
||||
|
||||
|
||||
QgsPostgresProvider::WriterError
|
||||
QgsPostgresProvider::importVector(
|
||||
QgsVectorLayer *layer,
|
||||
const QString& uri,
|
||||
const QgsCoordinateReferenceSystem *destCRS,
|
||||
bool onlySelected,
|
||||
QString *errorMessage,
|
||||
bool skipAttributeCreation )
|
||||
{
|
||||
const QgsCoordinateReferenceSystem* outputCRS;
|
||||
QgsCoordinateTransform* ct = 0;
|
||||
int shallTransform = false;
|
||||
|
||||
if ( !layer )
|
||||
{
|
||||
return ErrInvalidLayer;
|
||||
}
|
||||
|
||||
if ( destCRS && destCRS->isValid() )
|
||||
{
|
||||
// This means we should transform
|
||||
outputCRS = destCRS;
|
||||
shallTransform = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This means we shouldn't transform, use source CRS as output (if defined)
|
||||
outputCRS = &layer->crs();
|
||||
}
|
||||
|
||||
QgsFieldMap fields = skipAttributeCreation ? QgsFieldMap() : layer->pendingFields();
|
||||
|
||||
// populate members from the uri structure
|
||||
QgsDataSourceURI dsUri( uri );
|
||||
QString schemaName = dsUri.schema();
|
||||
QString tableName = dsUri.table();
|
||||
|
||||
QString geometryColumn = dsUri.geometryColumn();
|
||||
QString geometryType;
|
||||
|
||||
QString primaryKey = dsUri.keyColumn();
|
||||
QString primaryKeyType;
|
||||
|
||||
QString schemaTableName = "";
|
||||
if ( !schemaName.isEmpty() )
|
||||
{
|
||||
schemaTableName += QgsPostgresProvider::quotedIdentifier( schemaName ) + ".";
|
||||
}
|
||||
schemaTableName += QgsPostgresProvider::quotedIdentifier( tableName );
|
||||
|
||||
QgsDebugMsg( "Connection info is " + dsUri.connectionInfo() );
|
||||
QgsDebugMsg( "Geometry column is: " + geometryColumn );
|
||||
QgsDebugMsg( "Schema is: " + schemaName );
|
||||
QgsDebugMsg( "Table name is: " + tableName );
|
||||
|
||||
// create the table
|
||||
{
|
||||
Conn *conn = Conn::connectDb( dsUri.connectionInfo(), false );
|
||||
if ( conn == NULL )
|
||||
{
|
||||
QgsDebugMsg( "Connection to database failed. Import of layer aborted." );
|
||||
if ( errorMessage )
|
||||
*errorMessage = QObject::tr( "Connection to database failed" );
|
||||
return ErrConnectionFailed;
|
||||
}
|
||||
|
||||
// get the pk's name and type
|
||||
if ( primaryKey.isEmpty() )
|
||||
{
|
||||
// if no pk name was passed, define the new pk field name
|
||||
int index = 0;
|
||||
QString pk = primaryKey = "pk";
|
||||
for ( QgsFieldMap::const_iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
|
||||
{
|
||||
if ( fldIt.value().name() == pk )
|
||||
{
|
||||
// it already exists, try again with a new name
|
||||
primaryKey = QString( "%1_%2" ).arg( pk ).arg( index++ );
|
||||
fldIt = fields.begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// search for the passed field
|
||||
for ( QgsFieldMap::const_iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
|
||||
{
|
||||
if ( fldIt.value().name() == primaryKey )
|
||||
{
|
||||
// found, get the field type
|
||||
primaryKeyType = fldIt.value().typeName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the field doesn't not exist yet, create it as a serial field
|
||||
if ( primaryKeyType.isEmpty() )
|
||||
{
|
||||
primaryKeyType = "serial";
|
||||
// check the feature count to choose if create a serial8 pk field
|
||||
if ( layer->featureCount() > 0x10000 - 0x00FF )
|
||||
{
|
||||
primaryKeyType = "serial8";
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
conn->PQexecNR( "BEGIN" );
|
||||
|
||||
// delete the table if exists, then re-create it
|
||||
QString sql = QString( "SELECT %1(%2, %3) "
|
||||
"FROM pg_class AS cls JOIN pg_namespace AS nsp"
|
||||
" ON nsp.oid = cls.relnamespace "
|
||||
" WHERE cls.relname = %4 AND nsp.nspname = %5" )
|
||||
.arg( conn->majorVersion() < 2 ? "dropgeometrytable" : "st_dropgeometrytable" )
|
||||
.arg( quotedValue( schemaName ) )
|
||||
.arg( quotedValue( tableName ) )
|
||||
.arg( quotedValue( tableName ) )
|
||||
.arg( quotedValue( schemaName ) );
|
||||
|
||||
PGresult *result = conn->PQexec( sql );
|
||||
if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
|
||||
throw PGException( result );
|
||||
PQclear( result );
|
||||
|
||||
sql = QString( "CREATE TABLE %1 (%2 %3 PRIMARY KEY)" )
|
||||
.arg( schemaTableName )
|
||||
.arg( quotedIdentifier( primaryKey ) )
|
||||
.arg( primaryKeyType );
|
||||
|
||||
result = conn->PQexec( sql );
|
||||
if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
|
||||
throw PGException( result );
|
||||
PQclear( result );
|
||||
|
||||
// get geometry type, dim and srid
|
||||
int dim = 2;
|
||||
long srid = outputCRS->postgisSrid();
|
||||
|
||||
switch( layer->wkbType() )
|
||||
{
|
||||
case QGis::WKBPoint25D:
|
||||
dim = 3;
|
||||
case QGis::WKBPoint:
|
||||
geometryType = "POINT";
|
||||
break;
|
||||
|
||||
case QGis::WKBLineString25D:
|
||||
dim = 3;
|
||||
case QGis::WKBLineString:
|
||||
geometryType = "LINESTRING";
|
||||
break;
|
||||
|
||||
case QGis::WKBPolygon25D:
|
||||
dim = 3;
|
||||
case QGis::WKBPolygon:
|
||||
geometryType = "POLYGON";
|
||||
break;
|
||||
|
||||
case QGis::WKBMultiPoint25D:
|
||||
dim = 3;
|
||||
case QGis::WKBMultiPoint:
|
||||
geometryType = "MULTIPOINT";
|
||||
break;
|
||||
|
||||
case QGis::WKBMultiLineString25D:
|
||||
dim = 3;
|
||||
case QGis::WKBMultiLineString:
|
||||
geometryType = "MULTILINESTRING";
|
||||
break;
|
||||
|
||||
case QGis::WKBMultiPolygon25D:
|
||||
dim = 3;
|
||||
case QGis::WKBMultiPolygon:
|
||||
geometryType = "MULTIPOLYGON";
|
||||
break;
|
||||
|
||||
case QGis::WKBUnknown:
|
||||
geometryType = "GEOMETRY";
|
||||
break;
|
||||
|
||||
case QGis::WKBNoGeometry:
|
||||
default:
|
||||
dim = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// create geometry column
|
||||
if ( !geometryType.isEmpty() && dim > 0 )
|
||||
{
|
||||
sql = QString( "SELECT %1(%2, %3, %4, %5, %6, %7)" )
|
||||
.arg( conn->majorVersion() < 2 ? "addgeometrycolumn" : "st_addgeometrycolumn" )
|
||||
.arg( quotedValue( schemaName ) )
|
||||
.arg( QgsPostgresProvider::quotedValue( tableName ) )
|
||||
.arg( QgsPostgresProvider::quotedValue( geometryColumn ) )
|
||||
.arg( srid )
|
||||
.arg( QgsPostgresProvider::quotedValue( geometryType ) )
|
||||
.arg( dim );
|
||||
|
||||
PGresult *result = conn->PQexec( sql );
|
||||
if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
|
||||
throw PGException( result );
|
||||
PQclear( result );
|
||||
}
|
||||
else
|
||||
{
|
||||
geometryColumn = QString();
|
||||
geometryType = QString();
|
||||
dim = 0;
|
||||
}
|
||||
|
||||
conn->PQexecNR( "COMMIT" );
|
||||
}
|
||||
catch ( PGException &e )
|
||||
{
|
||||
QgsDebugMsg( "creation of data source " + schemaTableName + " failed. " + e.errorMessage() );
|
||||
if ( errorMessage )
|
||||
*errorMessage = QObject::tr( "creation of data source %1 failed. %2" )
|
||||
.arg( schemaTableName )
|
||||
.arg( e.errorMessage() );
|
||||
|
||||
conn->PQexecNR( "ROLLBACK" );
|
||||
Conn::disconnectRW( conn );
|
||||
return ErrCreateLayer;
|
||||
}
|
||||
Conn::disconnectRW( conn );
|
||||
|
||||
QgsDebugMsg( "layer " + schemaTableName + " created." );
|
||||
}
|
||||
|
||||
// use the provider to edit the table
|
||||
dsUri.setDataSource( schemaName, tableName, geometryColumn, QString(), primaryKey );
|
||||
QgsPostgresProvider *provider = new QgsPostgresProvider( dsUri.uri() );
|
||||
if ( !provider->isValid() )
|
||||
{
|
||||
QgsDebugMsg( "The layer " + schemaTableName + " just created is not valid or not supported by the provider." );
|
||||
if ( errorMessage )
|
||||
*errorMessage = QObject::tr( "loading of the layer %1 failed" )
|
||||
.arg( schemaTableName );
|
||||
|
||||
delete provider;
|
||||
return ErrCreateLayer;
|
||||
}
|
||||
|
||||
QgsDebugMsg( "layer loaded" );
|
||||
|
||||
// add fields to the layer
|
||||
{
|
||||
// get the list of fields
|
||||
QList<QgsField> flist;
|
||||
for ( QgsFieldMap::iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
|
||||
{
|
||||
QgsField &fld = fldIt.value();
|
||||
if ( fld.name() == primaryKey )
|
||||
continue;
|
||||
|
||||
if ( fld.name() == geometryColumn )
|
||||
{
|
||||
QgsDebugMsg( "Found a field with the same name of the geometry column. Skip it!" );
|
||||
continue;
|
||||
}
|
||||
|
||||
// convert field type only if the source provider is different from the destination one
|
||||
// because if both source and destination use the same provider,
|
||||
// the field type should be natively supported
|
||||
if ( layer->dataProvider()->name() != provider->name() )
|
||||
{
|
||||
QString fieldType = "varchar"; //default to string
|
||||
int fieldSize = fld.length();
|
||||
int fieldPrec = fld.precision();
|
||||
switch ( fld.type() )
|
||||
{
|
||||
case QVariant::LongLong:
|
||||
fieldType = "int8";
|
||||
fieldSize = -1;
|
||||
fieldPrec = 0;
|
||||
break;
|
||||
|
||||
case QVariant::String:
|
||||
fieldType = "varchar";
|
||||
fieldPrec = -1;
|
||||
break;
|
||||
|
||||
case QVariant::Int:
|
||||
fieldType = "int";
|
||||
fieldSize = -1;
|
||||
fieldPrec = 0;
|
||||
break;
|
||||
|
||||
case QVariant::Double:
|
||||
if ( fieldSize <= 0 || fieldPrec < 0)
|
||||
{
|
||||
fieldType = "real8";
|
||||
fieldSize = -1;
|
||||
fieldPrec = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fieldType = "decimal";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
QgsDebugMsg( "error creating field " + fld.name() + ": unsupported type" );
|
||||
if ( errorMessage )
|
||||
*errorMessage = QObject::tr( "unsupported type for field %1" )
|
||||
.arg( fld.name() );
|
||||
|
||||
delete provider;
|
||||
return ErrAttributeTypeUnsupported;
|
||||
}
|
||||
|
||||
fld.setTypeName( fieldType );
|
||||
fld.setLength( fieldSize );
|
||||
fld.setPrecision( fieldPrec );
|
||||
}
|
||||
|
||||
flist.append( fld );
|
||||
QgsDebugMsg( "creating field " + fld.name() +
|
||||
" type " + QString( QVariant::typeToName( fld.type() ) ) +
|
||||
" width " + QString::number( fld.length() ) +
|
||||
" precision " + QString::number( fld.precision() ) );
|
||||
}
|
||||
|
||||
if ( !provider->addAttributes( flist ) )
|
||||
{
|
||||
QgsDebugMsg( "error creating fields " );
|
||||
if ( errorMessage )
|
||||
*errorMessage = QObject::tr( "creation of fields failed" );
|
||||
|
||||
delete provider;
|
||||
return ErrAttributeCreationFailed;
|
||||
}
|
||||
|
||||
QgsDebugMsg( "Done creating fields" );
|
||||
}
|
||||
|
||||
QgsAttributeList allAttr = skipAttributeCreation ? QgsAttributeList() : layer->pendingAllAttributesList();
|
||||
QgsFeature fet;
|
||||
|
||||
layer->select( allAttr, QgsRectangle(), layer->wkbType() != QGis::WKBNoGeometry );
|
||||
|
||||
const QgsFeatureIds& ids = layer->selectedFeaturesIds();
|
||||
|
||||
// Create our transform
|
||||
if ( destCRS )
|
||||
{
|
||||
ct = new QgsCoordinateTransform( layer->crs(), *destCRS );
|
||||
}
|
||||
|
||||
// Check for failure
|
||||
if ( ct == NULL )
|
||||
{
|
||||
shallTransform = false;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
QStringList errors;
|
||||
|
||||
|
||||
// make all the changes using one transaction only
|
||||
if ( provider->connectRW() )
|
||||
{
|
||||
provider->connectionRW->PQexecNR( "BEGIN" );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsDebugMsg( "unable to estabilish a RW connection" );
|
||||
if ( errorMessage )
|
||||
*errorMessage = QObject::tr( "unable to estabilish a RW connection. Writing of features failed" );
|
||||
|
||||
delete provider;
|
||||
return ErrFeatureWriteFailed;
|
||||
}
|
||||
|
||||
// write all features
|
||||
QgsFeatureList flist;
|
||||
while (1)
|
||||
{
|
||||
while ( layer->nextFeature( fet ) )
|
||||
{
|
||||
if ( onlySelected && !ids.contains( fet.id() ) )
|
||||
continue;
|
||||
|
||||
if ( shallTransform )
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( fet.geometry() )
|
||||
{
|
||||
fet.geometry()->transform( *ct );
|
||||
}
|
||||
}
|
||||
catch ( QgsCsException &e )
|
||||
{
|
||||
delete ct;
|
||||
delete provider;
|
||||
|
||||
QString msg = QObject::tr( "Failed to transform a point while drawing a feature of type '%1'. Writing stopped. (Exception: %2)" )
|
||||
.arg( fet.typeName() ).arg( e.what() );
|
||||
QgsLogger::warning( msg );
|
||||
if ( errorMessage )
|
||||
*errorMessage = msg;
|
||||
|
||||
provider->connectionRW->PQexecNR( "ROLLBACK" );
|
||||
delete provider;
|
||||
return ErrProjection;
|
||||
}
|
||||
}
|
||||
if ( skipAttributeCreation )
|
||||
{
|
||||
fet.clearAttributeMap();
|
||||
}
|
||||
else
|
||||
{
|
||||
// fix attributes position based on how the table was created.
|
||||
// The first field is reserved to the primary key and the second one to
|
||||
// the geometry column (if any), so move the other attributes after these fields
|
||||
int offset = geometryColumn.isEmpty() ? 1 : 2;
|
||||
|
||||
const QgsAttributeMap &attrs = fet.attributeMap();
|
||||
QgsAttributeMap newAttrs;
|
||||
for ( QgsAttributeMap::const_iterator it = attrs.begin(); it != attrs.end(); it++ )
|
||||
{
|
||||
const QgsField &fld = fields[ it.key() ];
|
||||
if ( fld.name() == primaryKey || fld.name() == geometryColumn )
|
||||
continue;
|
||||
|
||||
newAttrs.insert( offset++, *it );
|
||||
}
|
||||
fet.setAttributeMap( newAttrs );
|
||||
|
||||
}
|
||||
|
||||
flist.append( fet );
|
||||
|
||||
// add 100 features at the same time
|
||||
if ( flist.size() == 100 )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !provider->addFeatures( flist, false ) )
|
||||
{
|
||||
QgsDebugMsg( QString( "failed while adding features from %1 to %2" )
|
||||
.arg( flist.first().id() )
|
||||
.arg( flist.last().id() )
|
||||
);
|
||||
errors << QObject::tr( "failed while adding features from %1 to %2" )
|
||||
.arg( flist.first().id() )
|
||||
.arg( flist.last().id() );
|
||||
}
|
||||
n += flist.size();
|
||||
|
||||
if ( flist.size() < 100 )
|
||||
break;
|
||||
flist.clear();
|
||||
}
|
||||
|
||||
provider->connectionRW->PQexecNR( "COMMIT" );
|
||||
delete provider;
|
||||
|
||||
if ( shallTransform )
|
||||
{
|
||||
delete ct;
|
||||
}
|
||||
|
||||
if ( errors.size() > 0 )
|
||||
{
|
||||
if ( errorMessage && n > 0 )
|
||||
{
|
||||
errors << QObject::tr( "Only %1 of %2 features written." ).arg( n - errors.size() ).arg( n );
|
||||
*errorMessage = errors.join( "\n" );
|
||||
}
|
||||
return ErrFeatureWriteFailed;
|
||||
}
|
||||
|
||||
if ( errorMessage )
|
||||
errorMessage->clear();
|
||||
|
||||
return NoError;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QgsPostgresProvider::QgsPostgresProvider( QString const & uri )
|
||||
: QgsVectorDataProvider( uri )
|
||||
, mFetching( false )
|
||||
@ -763,6 +1251,20 @@ QString QgsPostgresProvider::whereClause( QgsFeatureId featureId ) const
|
||||
bool QgsPostgresProvider::featureAtId( QgsFeatureId featureId, QgsFeature& feature, bool fetchGeometry, QgsAttributeList fetchAttributes )
|
||||
{
|
||||
feature.setValid( false );
|
||||
|
||||
#if 0
|
||||
if ( mFeatureMap.contains( featureId ) )
|
||||
{
|
||||
QgsFeature * fpointer = &feature;
|
||||
*fpointer = mFeatureMap.value( featureId );
|
||||
QgsDebugMsg( QString( "retrieve feature %1 from cache" ).arg( featureId ) );
|
||||
|
||||
mPriorityIds.removeAll( featureId );
|
||||
mPriorityIds.prepend( featureId );
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
QString cursorName = QString( "qgisfid%1" ).arg( providerId );
|
||||
|
||||
if ( !declareCursor( cursorName, fetchAttributes, fetchGeometry, whereClause( featureId ) ) )
|
||||
@ -789,6 +1291,19 @@ bool QgsPostgresProvider::featureAtId( QgsFeatureId featureId, QgsFeature& featu
|
||||
connectionRO->closeCursor( cursorName );
|
||||
|
||||
feature.setValid( gotit );
|
||||
|
||||
#if 0
|
||||
if ( gotit )
|
||||
{
|
||||
mFeatureMap.insert( featureId, feature );
|
||||
mPriorityIds.prepend( featureId );
|
||||
if ( mPriorityIds.count() == 20 )
|
||||
{
|
||||
mFeatureMap.remove( mPriorityIds.takeLast() );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return gotit;
|
||||
}
|
||||
|
||||
@ -2373,7 +2888,122 @@ QString QgsPostgresProvider::paramValue( QString fieldValue, const QString &defa
|
||||
return fieldValue;
|
||||
}
|
||||
|
||||
/*
|
||||
bool QgsPostgresProvider::createAddFeaturesPreparedStmt( const QgsFieldMap &fields, bool useNewTransaction, QString preparedStmtName )
|
||||
{
|
||||
if ( isQuery )
|
||||
return false;
|
||||
|
||||
// if there's no rw connection opened then create a new transaction
|
||||
if ( !connectionRW )
|
||||
useNewTransaction = true;
|
||||
|
||||
if ( !connectRW() )
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
if ( useNewTransaction )
|
||||
connectionRW->PQexecNR( "BEGIN" );
|
||||
|
||||
// Prepare the INSERT statement
|
||||
QString insert = QString( "INSERT INTO %1 (" ).arg( mQuery );
|
||||
QString values = ") VALUES (";
|
||||
QString delim = ",";
|
||||
int offset = 1;
|
||||
|
||||
if ( !geometryColumn.isNull() )
|
||||
{
|
||||
insert += quotedIdentifier( geometryColumn );
|
||||
values += QString( "%1($%2%3,%4)" )
|
||||
.arg( connectionRO->majorVersion() < 2 ? "geomfromwkb" : "st_geomfromwkb" )
|
||||
.arg( offset )
|
||||
.arg( connectionRW->useWkbHex() ? "" : "::bytea" )
|
||||
.arg( srid );
|
||||
offset += 1;
|
||||
delim = ",";
|
||||
}
|
||||
else
|
||||
{
|
||||
delim = "";
|
||||
}
|
||||
|
||||
if ( primaryKeyType != "tid" && primaryKeyType != "oid" )
|
||||
{
|
||||
insert += delim + quotedIdentifier( primaryKey );
|
||||
values += delim + QString( "$%1" ).arg( offset );
|
||||
offset += 1;
|
||||
delim = ",";
|
||||
}
|
||||
|
||||
QStringList defaultValues;
|
||||
QList<int> fieldId;
|
||||
|
||||
for ( QgsFieldMap::const_iterator it = fields.begin(); it != fields.end(); it++ )
|
||||
{
|
||||
QgsDebugMsg( QString( "field: %1" ).arg( it->name() ) );
|
||||
|
||||
QgsFieldMap::const_iterator fit = attributeFields.find( it.key() );
|
||||
if ( fit == attributeFields.end() )
|
||||
continue;
|
||||
|
||||
QString fieldname = fit->name();
|
||||
|
||||
if ( fieldname.isEmpty() || fieldname == geometryColumn || fieldname == primaryKey )
|
||||
continue;
|
||||
|
||||
insert += delim + quotedIdentifier( fieldname );
|
||||
|
||||
if ( fit->typeName() == "geometry" )
|
||||
{
|
||||
values += QString( "%1%2($%3)" )
|
||||
.arg( delim )
|
||||
.arg( connectionRO->majorVersion() < 2 ? "geomfromewkt" : "st_geomfromewkt" )
|
||||
.arg( fieldId.size() + offset );
|
||||
}
|
||||
else if ( fit->typeName() == "geography" )
|
||||
{
|
||||
values += QString( "%1st_geographyfromewkt($%2)" )
|
||||
.arg( delim )
|
||||
.arg( fieldId.size() + offset );
|
||||
}
|
||||
else
|
||||
{
|
||||
values += QString( "%1$%2" )
|
||||
.arg( delim )
|
||||
.arg( fieldId.size() + offset );
|
||||
}
|
||||
fieldId.append( it.key() );
|
||||
|
||||
delim = ",";
|
||||
}
|
||||
|
||||
insert += values + ")";
|
||||
|
||||
QgsDebugMsg( QString( "prepare %1: %2" ).arg( preparedStmtName ).arg( insert ) );
|
||||
PGresult *stmt = connectionRW->PQprepare( preparedStmtName, insert, fieldId.size() + offset - 1, NULL );
|
||||
if ( stmt == 0 || PQresultStatus( stmt ) == PGRES_FATAL_ERROR )
|
||||
throw PGException( stmt );
|
||||
PQclear( stmt );
|
||||
|
||||
}
|
||||
catch ( PGException &e )
|
||||
{
|
||||
e.showErrorMessage( tr( "Error while creating %1 prepared statement" ).arg( preparedStmtName ) );
|
||||
connectionRW->PQexecNR( "ROLLBACK" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )
|
||||
{
|
||||
return addFeatures( flist, true );
|
||||
}
|
||||
|
||||
bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, bool useNewTransaction )
|
||||
{
|
||||
if ( flist.size() == 0 )
|
||||
return true;
|
||||
@ -2381,6 +3011,10 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )
|
||||
if ( isQuery )
|
||||
return false;
|
||||
|
||||
// if there's no rw connection opened then create a new transaction
|
||||
if ( !connectionRW )
|
||||
useNewTransaction = true;
|
||||
|
||||
if ( !connectRW() )
|
||||
return false;
|
||||
|
||||
@ -2388,7 +3022,8 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )
|
||||
|
||||
try
|
||||
{
|
||||
connectionRW->PQexecNR( "BEGIN" );
|
||||
if ( useNewTransaction )
|
||||
connectionRW->PQexecNR( "BEGIN" );
|
||||
|
||||
// Prepare the INSERT statement
|
||||
QString insert = QString( "INSERT INTO %1 (" ).arg( mQuery );
|
||||
@ -2561,19 +3196,22 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )
|
||||
flist[i].setFeatureId( newIds[i] );
|
||||
|
||||
connectionRW->PQexecNR( "DEALLOCATE addfeatures" );
|
||||
connectionRW->PQexecNR( "COMMIT" );
|
||||
if ( useNewTransaction )
|
||||
connectionRW->PQexecNR( "COMMIT" );
|
||||
|
||||
featuresCounted += flist.size();
|
||||
}
|
||||
catch ( PGException &e )
|
||||
{
|
||||
e.showErrorMessage( tr( "Error while adding features" ) );
|
||||
connectionRW->PQexecNR( "ROLLBACK" );
|
||||
if ( useNewTransaction )
|
||||
connectionRW->PQexecNR( "ROLLBACK" );
|
||||
connectionRW->PQexecNR( "DEALLOCATE addfeatures" );
|
||||
returnvalue = false;
|
||||
}
|
||||
|
||||
rewind();
|
||||
if ( useNewTransaction )
|
||||
rewind();
|
||||
return returnvalue;
|
||||
}
|
||||
|
||||
@ -2619,29 +3257,41 @@ bool QgsPostgresProvider::deleteFeatures( const QgsFeatureIds & id )
|
||||
}
|
||||
|
||||
bool QgsPostgresProvider::addAttributes( const QList<QgsField> &attributes )
|
||||
{
|
||||
return addAttributes( attributes, true );
|
||||
}
|
||||
|
||||
bool QgsPostgresProvider::addAttributes( const QList<QgsField> &attributes, bool useNewTransaction )
|
||||
{
|
||||
bool returnvalue = true;
|
||||
|
||||
if ( isQuery )
|
||||
return false;
|
||||
|
||||
// if there's no rw connection opened then create a new transaction
|
||||
if ( !connectionRW )
|
||||
useNewTransaction = true;
|
||||
|
||||
if ( !connectRW() )
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
connectionRW->PQexecNR( "BEGIN" );
|
||||
if ( useNewTransaction )
|
||||
connectionRW->PQexecNR( "BEGIN" );
|
||||
|
||||
for ( QList<QgsField>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter )
|
||||
{
|
||||
QString type = iter->typeName();
|
||||
if ( type == "char" || type == "varchar" )
|
||||
{
|
||||
type = QString( "%1(%2)" ).arg( type ).arg( iter->length() );
|
||||
if ( iter->length() > 0 )
|
||||
type = QString( "%1(%2)" ).arg( type ).arg( iter->length() );
|
||||
}
|
||||
else if ( type == "numeric" || type == "decimal" )
|
||||
{
|
||||
type = QString( "%1(%2,%3)" ).arg( type ).arg( iter->length() ).arg( iter->precision() );
|
||||
if ( iter->length() > 0 && iter->precision() > 0 )
|
||||
type = QString( "%1(%2,%3)" ).arg( type ).arg( iter->length() ).arg( iter->precision() );
|
||||
}
|
||||
|
||||
QString sql = QString( "ALTER TABLE %1 ADD COLUMN %2 %3" )
|
||||
@ -2669,16 +3319,19 @@ bool QgsPostgresProvider::addAttributes( const QList<QgsField> &attributes )
|
||||
}
|
||||
}
|
||||
|
||||
connectionRW->PQexecNR( "COMMIT" );
|
||||
if ( useNewTransaction )
|
||||
connectionRW->PQexecNR( "COMMIT" );
|
||||
}
|
||||
catch ( PGException &e )
|
||||
{
|
||||
e.showErrorMessage( tr( "Error while adding attributes" ) );
|
||||
connectionRW->PQexecNR( "ROLLBACK" );
|
||||
if ( useNewTransaction )
|
||||
connectionRW->PQexecNR( "ROLLBACK" );
|
||||
returnvalue = false;
|
||||
}
|
||||
|
||||
rewind();
|
||||
if ( useNewTransaction )
|
||||
rewind();
|
||||
return returnvalue;
|
||||
}
|
||||
|
||||
@ -3421,13 +4074,13 @@ bool QgsPostgresProvider::getGeometryDetails()
|
||||
return valid;
|
||||
}
|
||||
|
||||
QString QgsPostgresProvider::quotedIdentifier( QString ident ) const
|
||||
QString QgsPostgresProvider::quotedIdentifier( QString ident )
|
||||
{
|
||||
ident.replace( '"', "\"\"" );
|
||||
return ident.prepend( "\"" ).append( "\"" );
|
||||
}
|
||||
|
||||
QString QgsPostgresProvider::quotedValue( QString value ) const
|
||||
QString QgsPostgresProvider::quotedValue( QString value )
|
||||
{
|
||||
if ( value.isNull() )
|
||||
return "NULL";
|
||||
@ -3669,3 +4322,14 @@ QGISEXTERN bool isProvider()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QGISEXTERN int importVector(
|
||||
QgsVectorLayer *layer,
|
||||
const QString& uri,
|
||||
const QgsCoordinateReferenceSystem *destCRS,
|
||||
bool onlySelected,
|
||||
QString *errorMessage,
|
||||
bool skipAttributeCreation )
|
||||
{
|
||||
return QgsPostgresProvider::importVector( layer, uri, destCRS, onlySelected, errorMessage, skipAttributeCreation );
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ extern "C"
|
||||
class QgsFeature;
|
||||
class QgsField;
|
||||
class QgsGeometry;
|
||||
class QgsVectorLayer;
|
||||
|
||||
#include "qgsdatasourceuri.h"
|
||||
|
||||
@ -48,6 +49,30 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum WriterError
|
||||
{
|
||||
NoError = 0,
|
||||
ErrDriverNotFound,
|
||||
ErrCreateDataSource,
|
||||
ErrCreateLayer,
|
||||
ErrAttributeTypeUnsupported,
|
||||
ErrAttributeCreationFailed,
|
||||
ErrProjection,
|
||||
ErrFeatureWriteFailed,
|
||||
ErrInvalidLayer,
|
||||
ErrConnectionFailed,
|
||||
};
|
||||
|
||||
/** Import a vector layer into the database */
|
||||
static WriterError importVector( QgsVectorLayer* layer,
|
||||
const QString& uri,
|
||||
const QgsCoordinateReferenceSystem *destCRS,
|
||||
bool onlySelected = false,
|
||||
QString *errorMessage = 0,
|
||||
bool skipAttributeCreation = false
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor for the provider. The uri must be in the following format:
|
||||
* host=localhost user=gsherman dbname=test password=xxx table=test.alaska (the_geom)
|
||||
@ -295,6 +320,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
*/
|
||||
QString description() const;
|
||||
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This is emitted whenever the worker thread has fully calculated the
|
||||
@ -316,6 +342,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
void repaintRequested();
|
||||
|
||||
private:
|
||||
|
||||
int providerId; // id to append to provider specific identified (like cursors)
|
||||
|
||||
bool declareCursor( const QString &cursorName,
|
||||
@ -337,11 +364,11 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
|
||||
/** Double quote a PostgreSQL identifier for placement in a SQL string.
|
||||
*/
|
||||
QString quotedIdentifier( QString ident ) const;
|
||||
static QString quotedIdentifier( QString ident );
|
||||
|
||||
/** Quote a value for placement in a SQL string.
|
||||
*/
|
||||
QString quotedValue( QString value ) const;
|
||||
static QString quotedValue( QString value );
|
||||
|
||||
/** expression to retrieve value
|
||||
*/
|
||||
@ -351,6 +378,17 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
*/
|
||||
bool loadFields();
|
||||
|
||||
/**Adds new attributes
|
||||
@param name map with attribute name as key and type as value
|
||||
@param useNewTransaction create a new transaction
|
||||
@return true in case of success and false in case of failure*/
|
||||
bool addAttributes( const QList<QgsField> &attributes, bool useNewTransaction );
|
||||
|
||||
/**Adds a list of features
|
||||
@param useNewTransaction create a new transaction
|
||||
@return true in case of success and false in case of failure*/
|
||||
bool addFeatures( QgsFeatureList & flist, bool useNewTransaction );
|
||||
|
||||
/**Parses the enum_range of an attribute and inserts the possible values into a stringlist
|
||||
@param enumValues the stringlist where the values are appended
|
||||
@param attributeName the name of the enum attribute
|
||||
@ -712,6 +750,13 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
* Default value for primary key
|
||||
*/
|
||||
QString mPrimaryKeyDefault;
|
||||
|
||||
#if 0
|
||||
/** used to cache the lastest fetched features */
|
||||
QHash<QgsFeatureId, QgsFeature> mFeatureMap;
|
||||
QList<QgsFeatureId> mPriorityIds;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user