[FEATURE][API] postgres provider refactoring:

- use m prefix for member variables and s prefix for class variables
- refactor QgsPostgresProvider::Conn and QgsPostgresConnection to QgsPostgresConn
- put QgsColumnTypeThread into separate file
- use QgsPostgresConn in QgsPgSourceSelect
- cleaner abort of column type thread (e.g. remove unfinished columns from
  selection list)
- [API] move QgsDbTableModel as QgsPgTableModel to provider
- [FEATURE] support for arbitrary key (including non-numeric and multi column)
- [FEATURE][API] support for requesting a certain geometry type and/or srid in QgsDataSourceURI
 * no more explicit filter strings
 * support for empty tables without gemetry_columns entry or GEOMETRY type.
This commit is contained in:
Juergen E. Fischer 2011-12-29 00:02:47 +01:00
parent c099afc241
commit ce4fc1daa5
24 changed files with 5043 additions and 5188 deletions

View File

@ -378,13 +378,10 @@ QPixmap QgsLegendLayer::getOriginalPixmap()
{
case QGis::Point:
return QgisApp::getThemePixmap( "/mIconPointLayer.png" );
break;
case QGis::Line:
return QgisApp::getThemePixmap( "/mIconLineLayer.png" );
break;
case QGis::Polygon:
return QgisApp::getThemePixmap( "/mIconPolygonLayer.png" );
break;
case QGis::NoGeometry:
return QgisApp::getThemePixmap( "/mIconTableLayer.png" );
default:

View File

@ -54,7 +54,6 @@ SET(QGIS_CORE_SRCS
qgsdatasourceuri.cpp
qgsdataitem.cpp
qgsdbfilterproxymodel.cpp
qgsdbtablemodel.cpp
qgsdiagram.cpp
qgsdiagramrendererv2.cpp
qgsdistancearea.cpp
@ -235,7 +234,6 @@ SET(QGIS_CORE_MOC_HDRS
qgscontexthelp.h
qgscoordinatetransform.h
qgsdataitem.h
qgsdbtablemodel.h
qgsdataprovider.h
qgshttptransaction.h
qgsmaplayer.h

View File

@ -90,7 +90,7 @@ class CORE_EXPORT QGis
DegreesMinutesSeconds = 4,
DegreesDecimalMinutes = 5,
UnknownUnit = 3
} ;
};
//! User defined event types
enum UserEvent

View File

@ -27,6 +27,7 @@ QgsDataSourceURI::QgsDataSourceURI()
, mKeyColumn( "" )
, mUseEstimatedMetadata( false )
, mSelectAtIdDisabled( false )
, mGeometryType( QGis::WKBUnknown )
{
// do nothing
}
@ -36,6 +37,7 @@ QgsDataSourceURI::QgsDataSourceURI( QString uri )
, mKeyColumn( "" )
, mUseEstimatedMetadata( false )
, mSelectAtIdDisabled( false )
, mGeometryType( QGis::WKBUnknown )
{
int i = 0;
while ( i < uri.length() )
@ -123,6 +125,42 @@ QgsDataSourceURI::QgsDataSourceURI( QString uri )
{
mUseEstimatedMetadata = pval == "true";
}
else if ( pname == "srid" )
{
mSrid = pval;
}
else if ( pname == "type" )
{
QString geomTypeUpper = pval.toUpper();
if ( geomTypeUpper == "POINT" )
{
mGeometryType = QGis::WKBPoint;
}
else if ( geomTypeUpper == "MULTIPOINT" )
{
mGeometryType = QGis::WKBMultiPoint;
}
else if ( geomTypeUpper == "LINESTRING" )
{
mGeometryType = QGis::WKBLineString;
}
else if ( geomTypeUpper == "MULTILINESTRING" )
{
mGeometryType = QGis::WKBMultiLineString;
}
else if ( geomTypeUpper == "POLYGON" )
{
mGeometryType = QGis::WKBPolygon;
}
else if ( geomTypeUpper == "MULTIPOLYGON" )
{
mGeometryType = QGis::WKBMultiPolygon;
}
else
{
mGeometryType = QGis::WKBUnknown;
}
}
else if ( pname == "selectatid" )
{
mSelectAtIdDisabled = pval == "false";
@ -483,6 +521,16 @@ QString QgsDataSourceURI::uri() const
theUri += QString( " estimatedmetadata=true" );
}
if ( !mSrid.isEmpty() )
{
theUri += QString( " srid=%1" ).arg( mSrid );
}
if ( mGeometryType != QGis::WKBUnknown && mGeometryType != QGis::WKBNoGeometry )
{
theUri += QString( " type=%1" ).arg( QGis::qgisFeatureTypes[mGeometryType] + 3 );
}
if ( mSelectAtIdDisabled )
{
theUri += QString( " selectatid=false" );
@ -552,3 +600,23 @@ void QgsDataSourceURI::setDatabase( const QString &database )
{
mDatabase = database;
}
QGis::WkbType QgsDataSourceURI::geometryType() const
{
return mGeometryType;
}
void QgsDataSourceURI::setGeometryType( QGis::WkbType geometryType )
{
mGeometryType = geometryType;
}
QString QgsDataSourceURI::srid() const
{
return mSrid;
}
void QgsDataSourceURI::setSrid( QString srid )
{
mSrid = srid;
}

View File

@ -19,7 +19,7 @@
#ifndef QGSDATASOURCEURI_H
#define QGSDATASOURCEURI_H
#include <QString>
#include "qgis.h"
/** \ingroup core
* Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
@ -117,6 +117,12 @@ class CORE_EXPORT QgsDataSourceURI
QString keyColumn() const;
void setKeyColumn( QString column );
// added in 1.9
QGis::WkbType geometryType() const;
void setGeometryType( QGis::WkbType type );
QString srid() const;
void setSrid( QString srid );
private:
void skipBlanks( const QString &uri, int &i );
QString getValue( const QString &uri, int &i );
@ -148,10 +154,14 @@ class CORE_EXPORT QgsDataSourceURI
enum SSLmode mSSLmode;
//! key column
QString mKeyColumn;
//Use estimated metadata flag
//! Use estimated metadata flag
bool mUseEstimatedMetadata;
//Disable SelectAtId capability (eg. to trigger the attribute table memory model for expensive views)
//! Disable SelectAtId capability (eg. to trigger the attribute table memory model for expensive views)
bool mSelectAtIdDisabled;
//! geometry type (or QGis::WKBUnknown if not specified)
QGis::WkbType mGeometryType;
//! SRID or a null string if not specified
QString mSrid;
};
#endif //QGSDATASOURCEURI_H

View File

@ -4,17 +4,21 @@
SET(PG_SRCS
qgspostgresprovider.cpp
qgspostgresconnection.cpp
qgspostgresconn.cpp
qgspostgresdataitems.cpp
qgspgsourceselect.cpp
qgspgnewconnection.cpp
qgspgtablemodel.cpp
qgscolumntypethread.cpp
)
SET(PG_MOC_HDRS
qgspostgresprovider.h
qgspostgresconnection.h
qgspostgresconn.h
qgspostgresdataitems.h
qgspgsourceselect.h
qgspgnewconnection.h
qgspgtablemodel.h
qgscolumntypethread.h
)

View File

@ -0,0 +1,64 @@
/***************************************************************************
qgscolumntypethread.cpp - lookup postgres geometry type and srid in a thread
-------------------
begin : 3.1.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 "qgscolumntypethread.h"
#include <QMetaType>
QgsGeomColumnTypeThread::QgsGeomColumnTypeThread( QgsPostgresConn *conn, bool useEstimatedMetaData )
: QThread()
, mConn( conn )
, mUseEstimatedMetadata( useEstimatedMetaData )
{
qRegisterMetaType<QgsPostgresLayerProperty>( "QgsPostgresLayerProperty" );
}
void QgsGeomColumnTypeThread::addGeometryColumn( QgsPostgresLayerProperty layerProperty )
{
layerProperties << layerProperty;
}
void QgsGeomColumnTypeThread::stop()
{
mStopped = true;
}
void QgsGeomColumnTypeThread::run()
{
if ( !mConn )
return;
mStopped = false;
foreach( QgsPostgresLayerProperty layerProperty, layerProperties )
{
if ( !mStopped )
{
mConn->retrieveLayerTypes( layerProperty, mUseEstimatedMetadata );
}
else
{
layerProperty.type = "";
}
// Now tell the layer list dialog box...
emit setLayerType( layerProperty );
}
mConn->disconnect();
mConn = 0;
}

View File

@ -0,0 +1,53 @@
/***************************************************************************
qgscolumntypethread.cpp - lookup postgres geometry type and srid in a thread
-------------------
begin : 3.1.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 QGSCOLUMNTYPETHREAD_H
#define QGSCOLUMNTYPETHREAD_H
#include <QThread>
#include "qgspostgresconn.h"
// A class that determines the geometry type of a given database
// schema.table.column, with the option of doing so in a separate
// thread.
class QgsGeomColumnTypeThread : public QThread
{
Q_OBJECT
public:
QgsGeomColumnTypeThread( QgsPostgresConn *conn, bool useEstimatedMetaData );
void addGeometryColumn( QgsPostgresLayerProperty layerProperty );
// These functions get the layer types and pass that information out
// by emitting the setLayerType() signal.
virtual void run();
signals:
void setLayerType( QgsPostgresLayerProperty layerProperty );
public slots:
void stop();
private:
QgsGeomColumnTypeThread() {}
QgsPostgresConn *mConn;
bool mUseEstimatedMetadata;
bool mStopped;
QList<QgsPostgresLayerProperty> layerProperties;
};
#endif // QGSCOLUMNTYPETHREAD_H

View File

@ -0,0 +1,53 @@
/***************************************************************************
qgsdbfilterproxymodel.cpp - description
-------------------------
begin : Dec 2007
copyright : (C) 2007 by Marco Hugentobler
email : marco dot hugentobler at karto dot baug dot ethz dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 "qgsdbfilterproxymodel.h"
QgsDbFilterProxyModel::QgsDbFilterProxyModel( QObject* parent ): QSortFilterProxyModel( parent )
{
}
QgsDbFilterProxyModel::~QgsDbFilterProxyModel()
{
}
bool QgsDbFilterProxyModel::filterAcceptsRow( int row, const QModelIndex & source_parent ) const
{
//if parent is valid, we have a toplevel item that should be always shown
if ( !source_parent.isValid() )
{
return true;
}
//else we have a row that describes a table and that
//should be tested using the given wildcard/regexp
return QSortFilterProxyModel::filterAcceptsRow( row, source_parent );
}
void QgsDbFilterProxyModel::_setFilterWildcard( const QString& pattern )
{
QSortFilterProxyModel::setFilterWildcard( pattern );
emit layoutChanged();
}
void QgsDbFilterProxyModel::_setFilterRegExp( const QString& pattern )
{
QSortFilterProxyModel::setFilterRegExp( pattern );
emit layoutChanged();
}

View File

@ -0,0 +1,39 @@
/***************************************************************************
qgsdbfilterproxymodel.h - description
-----------------------
begin : Dec 2007
copyright : (C) 2007 by Marco Hugentobler
email : marco dot hugentobler at karto dot baug dot ethz dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 QGSDBFILTERPROXYMODEL_H
#define QGSDBFILTERPROXYMODEL_H
#include <QSortFilterProxyModel>
/**A class that implements a custom filter and can be used
as a proxy for QgsDbTableModel*/
class CORE_EXPORT QgsDbFilterProxyModel: public QSortFilterProxyModel
{
public:
QgsDbFilterProxyModel( QObject* parent = 0 );
~QgsDbFilterProxyModel();
/**Calls QSortFilterProxyModel::setFilterWildcard and triggers update*/
void _setFilterWildcard( const QString& pattern );
/**Calls QSortFilterProxyModel::setFilterRegExp and triggers update*/
void _setFilterRegExp( const QString& pattern );
protected:
virtual bool filterAcceptsRow( int row, const QModelIndex & source_parent ) const;
};
#endif

View File

@ -22,13 +22,7 @@
#include "qgspgnewconnection.h"
#include "qgscontexthelp.h"
#include "qgsdatasourceuri.h"
#include "qgslogger.h"
#include "qgscredentialdialog.h"
extern "C"
{
#include <libpq-fe.h>
}
#include "qgspostgresconn.h"
QgsPgNewConnection::QgsPgNewConnection( QWidget *parent, const QString& connName, Qt::WFlags fl )
: QDialog( parent, fl ), mOriginalConnName( connName )
@ -175,63 +169,23 @@ void QgsPgNewConnection::testConnection()
( QgsDataSourceURI::SSLmode ) cbxSSLmode->itemData( cbxSSLmode->currentIndex() ).toInt() );
}
QString conninfo = uri.connectionInfo();
QgsDebugMsg( "PQconnectdb(\"" + conninfo + "\");" );
PGconn *pd = PQconnectdb( conninfo.toLocal8Bit() ); // use what is set based on locale; after connecting, use Utf8
// check the connection status
if ( PQstatus( pd ) != CONNECTION_OK )
{
QString username = txtUsername->text();
QString password = txtPassword->text();
QgsPostgresConn *conn = QgsPostgresConn::connectDb( conninfo, true );
uri.setUsername( "" );
uri.setPassword( "" );
while ( PQstatus( pd ) != CONNECTION_OK )
{
bool ok = QgsCredentials::instance()->get( conninfo, username, password, QString::fromUtf8( PQerrorMessage( pd ) ) );
if ( !ok )
break;
::PQfinish( pd );
QgsDataSourceURI uri( conninfo );
if ( !username.isEmpty() )
uri.setUsername( username );
if ( !password.isEmpty() )
uri.setPassword( password );
QgsDebugMsg( "PQconnectdb(\"" + uri.connectionInfo() + "\");" );
pd = PQconnectdb( uri.connectionInfo().toLocal8Bit() );
}
if ( PQstatus( pd ) == CONNECTION_OK )
{
if ( chkStoreUsername->isChecked() )
txtUsername->setText( username );
if ( chkStorePassword->isChecked() )
txtPassword->setText( password );
QgsCredentials::instance()->put( conninfo, username, password );
}
}
if ( PQstatus( pd ) == CONNECTION_OK )
if ( conn )
{
// Database successfully opened; we can now issue SQL commands.
QMessageBox::information( this,
tr( "Test connection" ),
tr( "Connection to %1 was successful" ).arg( txtDatabase->text() ) );
// free pg connection resources
conn->disconnect();
}
else
{
QMessageBox::information( this,
tr( "Test connection" ),
tr( "Connection failed - Check settings and try again.\n\nExtended error information:\n%1" )
.arg( QString::fromUtf8( PQerrorMessage( pd ) ) ) );
tr( "Connection failed - Check settings and try again.\n\n" ) );
}
// free pg connection resources
PQfinish( pd );
}

View File

@ -21,14 +21,13 @@ email : sherman at mrcc.com
#include "qgslogger.h"
#include "qgsapplication.h"
#include "qgscontexthelp.h"
#include "qgspostgresconnection.h"
#include "qgspostgresprovider.h"
#include "qgspgnewconnection.h"
#include "qgsmanageconnectionsdialog.h"
#include "qgsquerybuilder.h"
#include "qgsdatasourceuri.h"
#include "qgsvectorlayer.h"
#include "qgscredentials.h"
#include "qgscolumntypethread.h"
#include <QFileDialog>
#include <QInputDialog>
@ -38,13 +37,76 @@ email : sherman at mrcc.com
#include <QHeaderView>
#include <QStringList>
#ifdef HAVE_PGCONFIG
#include <pg_config.h>
#endif
/** Used to create an editor for when the user tries to change the contents of a cell */
QWidget *QgsPgSourceSelectDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
Q_UNUSED( option );
if ( index.column() == QgsPgTableModel::dbtmSql )
{
QLineEdit *le = new QLineEdit( parent );
le->setText( index.data( Qt::DisplayRole ).toString() );
return le;
}
// Note: Because the the geometry type select SQL is also in the qgspostgresprovider
// code this parameter is duplicated there.
static const int sGeomTypeSelectLimit = 100;
if ( index.column() == QgsPgTableModel::dbtmType && index.data( Qt::UserRole + 1 ).toBool() )
{
QComboBox *cb = new QComboBox( parent );
foreach( QGis::WkbType type,
QList<QGis::WkbType>()
<< QGis::WKBPoint
<< QGis::WKBLineString
<< QGis::WKBPolygon
<< QGis::WKBMultiPoint
<< QGis::WKBMultiLineString
<< QGis::WKBMultiPolygon
<< QGis::WKBNoGeometry )
{
cb->addItem( QgsPgTableModel::iconForType( type ), QgsPgTableModel::displayStringForType( type ).toUpper(), type );
}
cb->setCurrentIndex( cb->findData( index.data( Qt::UserRole + 2 ).toInt() ) );
return cb;
}
if ( index.column() == QgsPgTableModel::dbtmPkCol )
{
QStringList values = index.data( Qt::UserRole + 1 ).toStringList();
if ( values.size() > 0 )
{
QComboBox *cb = new QComboBox( parent );
cb->addItems( values );
cb->setCurrentIndex( cb->findText( index.data( Qt::DisplayRole ).toString() ) );
return cb;
}
}
return 0;
}
void QgsPgSourceSelectDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
{
QComboBox *cb = qobject_cast<QComboBox *>( editor );
if ( cb )
{
if ( index.column() == QgsPgTableModel::dbtmType )
{
QGis::WkbType type = ( QGis::WkbType ) cb->itemData( cb->currentIndex() ).toInt();
model->setData( index, QgsPgTableModel::iconForType( type ), Qt::DecorationRole );
model->setData( index, type != QGis::WKBUnknown ? QgsPgTableModel::displayStringForType( type ) : tr( "Select..." ) );
model->setData( index, type, Qt::UserRole + 2 );
}
else if ( index.column() == QgsPgTableModel::dbtmPkCol )
{
model->setData( index, cb->currentText() );
model->setData( index, cb->currentText(), Qt::UserRole + 2 );
}
}
QLineEdit *le = qobject_cast<QLineEdit *>( editor );
if ( le )
model->setData( index, le->text() );
}
QgsPgSourceSelect::QgsPgSourceSelect( QWidget *parent, Qt::WFlags fl, bool managerMode, bool embeddedMode )
: QDialog( parent, fl )
@ -86,6 +148,7 @@ QgsPgSourceSelect::QgsPgSourceSelect( QWidget *parent, Qt::WFlags fl, bool manag
mSearchColumnComboBox->addItem( tr( "Type" ) );
mSearchColumnComboBox->addItem( tr( "Geometry column" ) );
mSearchColumnComboBox->addItem( tr( "Primary key column" ) );
mSearchColumnComboBox->addItem( tr( "SRID" ) );
mSearchColumnComboBox->addItem( tr( "Sql" ) );
mProxyModel.setParent( this );
@ -214,7 +277,7 @@ void QgsPgSourceSelect::on_btnEdit_clicked()
void QgsPgSourceSelect::on_cmbConnections_activated( int )
{
// Remember which database was selected.
QgsPostgresConnection::setSelectedConnection( cmbConnections->currentText() );
QgsPostgresConn::setSelectedConnection( cmbConnections->currentText() );
cbxAllowGeometrylessTables->blockSignals( true );
QSettings settings;
@ -270,27 +333,31 @@ void QgsPgSourceSelect::on_mSearchColumnComboBox_currentIndexChanged( const QStr
}
else if ( text == tr( "Schema" ) )
{
mProxyModel.setFilterKeyColumn( QgsDbTableModel::dbtmSchema );
mProxyModel.setFilterKeyColumn( QgsPgTableModel::dbtmSchema );
}
else if ( text == tr( "Table" ) )
{
mProxyModel.setFilterKeyColumn( QgsDbTableModel::dbtmTable );
mProxyModel.setFilterKeyColumn( QgsPgTableModel::dbtmTable );
}
else if ( text == tr( "Type" ) )
{
mProxyModel.setFilterKeyColumn( QgsDbTableModel::dbtmType );
mProxyModel.setFilterKeyColumn( QgsPgTableModel::dbtmType );
}
else if ( text == tr( "Geometry column" ) )
{
mProxyModel.setFilterKeyColumn( QgsDbTableModel::dbtmGeomCol );
mProxyModel.setFilterKeyColumn( QgsPgTableModel::dbtmGeomCol );
}
else if ( text == tr( "Primary key column" ) )
{
mProxyModel.setFilterKeyColumn( QgsDbTableModel::dbtmPkCol );
mProxyModel.setFilterKeyColumn( QgsPgTableModel::dbtmPkCol );
}
else if ( text == tr( "SRID" ) )
{
mProxyModel.setFilterKeyColumn( QgsPgTableModel::dbtmSrid );
}
else if ( text == tr( "Sql" ) )
{
mProxyModel.setFilterKeyColumn( QgsDbTableModel::dbtmSql );
mProxyModel.setFilterKeyColumn( QgsPgTableModel::dbtmSql );
}
}
@ -300,13 +367,11 @@ void QgsPgSourceSelect::on_mSearchModeComboBox_currentIndexChanged( const QStrin
on_mSearchTableEdit_textChanged( mSearchTableEdit->text() );
}
void QgsPgSourceSelect::setLayerType( QString schema,
QString table, QString column,
QString type )
void QgsPgSourceSelect::setLayerType( QgsPostgresLayerProperty layerProperty )
{
mTableModel.setGeometryTypesForTable( schema, table, column, type );
mTablesTreeView->sortByColumn( QgsDbTableModel::dbtmTable, Qt::AscendingOrder );
mTablesTreeView->sortByColumn( QgsDbTableModel::dbtmSchema, Qt::AscendingOrder );
mTableModel.setGeometryTypesForTable( layerProperty );
mTablesTreeView->sortByColumn( QgsPgTableModel::dbtmTable, Qt::AscendingOrder );
mTablesTreeView->sortByColumn( QgsPgTableModel::dbtmSchema, Qt::AscendingOrder );
}
QgsPgSourceSelect::~QgsPgSourceSelect()
@ -315,8 +380,6 @@ QgsPgSourceSelect::~QgsPgSourceSelect()
{
mColumnTypeThread->stop();
mColumnTypeThread->wait();
delete mColumnTypeThread;
mColumnTypeThread = NULL;
}
QSettings settings;
@ -330,7 +393,7 @@ QgsPgSourceSelect::~QgsPgSourceSelect()
void QgsPgSourceSelect::populateConnectionList()
{
QStringList keys = QgsPostgresConnection::connectionList();
QStringList keys = QgsPostgresConn::connectionList();
QStringList::Iterator it = keys.begin();
cmbConnections->clear();
while ( it != keys.end() )
@ -349,47 +412,31 @@ void QgsPgSourceSelect::populateConnectionList()
QString QgsPgSourceSelect::layerURI( const QModelIndex &index )
{
QString schemaName = mTableModel.itemFromIndex( index.sibling( index.row(), QgsDbTableModel::dbtmSchema ) )->text();
QString tableName = mTableModel.itemFromIndex( index.sibling( index.row(), QgsDbTableModel::dbtmTable ) )->text();
QString geomColumnName = mTableModel.itemFromIndex( index.sibling( index.row(), QgsDbTableModel::dbtmGeomCol ) )->text();
QString pkColumnName = mTableModel.itemFromIndex( index.sibling( index.row(), QgsDbTableModel::dbtmPkCol ) )->text();
bool selectAtId = mTableModel.itemFromIndex( index.sibling( index.row(), QgsDbTableModel::dbtmSelectAtId ) )->checkState() == Qt::Checked;
QString sql = mTableModel.itemFromIndex( index.sibling( index.row(), QgsDbTableModel::dbtmSql ) )->text();
QGis::WkbType geomType = ( QGis::WkbType ) mTableModel.itemFromIndex( index.sibling( index.row(), QgsPgTableModel::dbtmType ) )->data( Qt::UserRole + 2 ).toInt();
if ( geomType == QGis::WKBUnknown )
// no geometry type selected
return QString::null;
if ( geomColumnName.contains( " AS " ) )
{
int a = geomColumnName.indexOf( " AS " );
QString typeName = geomColumnName.mid( a + 4 ); //only the type name
geomColumnName = geomColumnName.left( a ); //only the geom column name
QString geomFilter;
QStandardItem *pkItem = mTableModel.itemFromIndex( index.sibling( index.row(), QgsPgTableModel::dbtmPkCol ) );
QString pkColumnName = pkItem->data( Qt::UserRole + 2 ).toString();
if ( typeName == "POINT" )
{
geomFilter = QString( "upper(geometrytype(\"%1\")) IN ('POINT','MULTIPOINT')" ).arg( geomColumnName );
}
else if ( typeName == "LINESTRING" )
{
geomFilter = QString( "upper(geometrytype(\"%1\")) IN ('LINESTRING','MULTILINESTRING')" ).arg( geomColumnName );
}
else if ( typeName == "POLYGON" )
{
geomFilter = QString( "upper(geometrytype(\"%1\")) IN ('POLYGON','MULTIPOLYGON')" ).arg( geomColumnName );
}
if ( pkItem->data( Qt::UserRole + 1 ).toStringList().size() > 0 && pkColumnName.isEmpty() )
// no primary key for view selected
return QString::null;
if ( !geomFilter.isEmpty() && !sql.contains( geomFilter ) )
{
if ( !sql.isEmpty() )
{
sql += " AND ";
}
QString schemaName = mTableModel.itemFromIndex( index.sibling( index.row(), QgsPgTableModel::dbtmSchema ) )->text();
QString tableName = mTableModel.itemFromIndex( index.sibling( index.row(), QgsPgTableModel::dbtmTable ) )->text();
QString geomColumnName = mTableModel.itemFromIndex( index.sibling( index.row(), QgsPgTableModel::dbtmGeomCol ) )->text();
sql += geomFilter;
}
}
QString srid = mTableModel.itemFromIndex( index.sibling( index.row(), QgsPgTableModel::dbtmSrid ) )->text();
bool selectAtId = mTableModel.itemFromIndex( index.sibling( index.row(), QgsPgTableModel::dbtmSelectAtId ) )->checkState() == Qt::Checked;
QString sql = mTableModel.itemFromIndex( index.sibling( index.row(), QgsPgTableModel::dbtmSql ) )->text();
QgsDataSourceURI uri( m_connInfo );
uri.setDataSource( schemaName, tableName, geomColumnName, sql, pkColumnName );
uri.setDataSource( schemaName, tableName, geomType != QGis::WKBNoGeometry ? geomColumnName : QString::null, sql, pkColumnName );
uri.setUseEstimatedMetadata( mUseEstimatedMetadata );
uri.setGeometryType( geomType );
uri.setSrid( srid );
uri.disableSelectAtId( !selectAtId );
return uri.uri();
@ -412,7 +459,11 @@ void QgsPgSourceSelect::addTables()
}
QModelIndex index = mProxyModel.mapToSource( *selected_it );
m_selectedTables << layerURI( index );
QString uri = layerURI( index );
if ( uri.isNull() )
continue;
m_selectedTables << uri;
}
if ( m_selectedTables.empty() )
@ -433,23 +484,22 @@ void QgsPgSourceSelect::on_btnConnect_clicked()
if ( mColumnTypeThread )
{
mColumnTypeThread->stop();
mColumnTypeThread = 0;
return;
}
QModelIndex rootItemIndex = mTableModel.indexFromItem( mTableModel.invisibleRootItem() );
mTableModel.removeRows( 0, mTableModel.rowCount( rootItemIndex ), rootItemIndex );
// populate the table list
QgsPostgresConnection connection( cmbConnections->currentText() );
QgsDataSourceURI uri( connection.connectionInfo() );
QgsDataSourceURI uri = QgsPostgresConn::connUri( cmbConnections->currentText() );
QgsDebugMsg( "Connection info: " + uri.connectionInfo() );
m_connInfo = uri.connectionInfo();
mUseEstimatedMetadata = uri.useEstimatedMetadata();
QgsPostgresProvider *pgProvider = connection.provider();
if ( pgProvider )
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo(), true );
if ( conn )
{
QApplication::setOverrideCursor( Qt::WaitCursor );
@ -461,43 +511,47 @@ void QgsPgSourceSelect::on_btnConnect_clicked()
bool allowGeometrylessTables = cbxAllowGeometrylessTables->isChecked();
QVector<QgsPostgresLayerProperty> layers;
if ( pgProvider->supportedLayers( layers, searchGeometryColumnsOnly, searchPublicOnly, allowGeometrylessTables ) )
if ( conn->supportedLayers( layers, searchGeometryColumnsOnly, searchPublicOnly, allowGeometrylessTables ) )
{
// Add the supported layers to the table
foreach( QgsPostgresLayerProperty layer, layers )
{
QString type = layer.type;
if ( !searchGeometryColumnsOnly && layer.geometryColName != QString::null )
if ( !searchGeometryColumnsOnly && !layer.geometryColName.isNull() )
{
if ( type == "GEOMETRY" || type == QString::null )
{
addSearchGeometryColumn( layer.schemaName, layer.tableName, layer.geometryColName );
type = tr( "Waiting" );
addSearchGeometryColumn( layer );
type = "";
}
}
QgsDebugMsg( QString( "adding table %1.%2" ).arg( layer.schemaName ).arg( layer.tableName ) );
mTableModel.addTableEntry( type, layer.schemaName, layer.tableName, layer.geometryColName, layer.pkCols, layer.sql );
layer.type = type;
mTableModel.addTableEntry( layer );
}
// Start the thread that gets the geometry type for relations that
// may take a long time to return
if ( mColumnTypeThread != NULL )
if ( mColumnTypeThread )
{
connect( mColumnTypeThread, SIGNAL( setLayerType( QString, QString, QString, QString ) ),
this, SLOT( setLayerType( QString, QString, QString, QString ) ) );
connect( mColumnTypeThread, SIGNAL( setLayerType( QgsPostgresLayerProperty ) ),
this, SLOT( setLayerType( QgsPostgresLayerProperty ) ) );
connect( mColumnTypeThread, SIGNAL( finished() ),
this, SLOT( columnThreadFinished() ) );
btnConnect->setText( tr( "Stop" ) );
// Do it in a thread.
mColumnTypeThread->start();
}
}
// BEGIN CHANGES ECOS
if ( cmbConnections->count() > 0 )
mAddButton->setEnabled( true );
// END CHANGES ECOS
mTablesTreeView->sortByColumn( QgsDbTableModel::dbtmTable, Qt::AscendingOrder );
mTablesTreeView->sortByColumn( QgsDbTableModel::dbtmSchema, Qt::AscendingOrder );
mTablesTreeView->sortByColumn( QgsPgTableModel::dbtmTable, Qt::AscendingOrder );
mTablesTreeView->sortByColumn( QgsPgTableModel::dbtmSchema, Qt::AscendingOrder );
//if we have only one schema item, expand it by default
int numTopLevelItems = mTableModel.invisibleRootItem()->rowCount();
@ -510,8 +564,10 @@ void QgsPgSourceSelect::on_btnConnect_clicked()
}
}
delete pgProvider;
QApplication::restoreOverrideCursor();
conn->disconnect();
if ( !mColumnTypeThread )
QApplication::restoreOverrideCursor();
}
else
{
@ -522,6 +578,14 @@ void QgsPgSourceSelect::on_btnConnect_clicked()
}
}
void QgsPgSourceSelect::columnThreadFinished()
{
delete mColumnTypeThread;
mColumnTypeThread = 0;
btnConnect->setText( tr( "Connect" ) );
QApplication::restoreOverrideCursor();
}
QStringList QgsPgSourceSelect::selectedTables()
{
return m_selectedTables;
@ -541,7 +605,7 @@ void QgsPgSourceSelect::setSql( const QModelIndex &index )
}
QModelIndex idx = mProxyModel.mapToSource( index );
QString tableName = mTableModel.itemFromIndex( idx.sibling( idx.row(), QgsDbTableModel::dbtmTable ) )->text();
QString tableName = mTableModel.itemFromIndex( idx.sibling( idx.row(), QgsPgTableModel::dbtmTable ) )->text();
QgsVectorLayer *vlayer = new QgsVectorLayer( layerURI( idx ), tableName, "postgres" );
@ -562,31 +626,34 @@ void QgsPgSourceSelect::setSql( const QModelIndex &index )
delete vlayer;
}
void QgsPgSourceSelect::addSearchGeometryColumn( const QString &schema, const QString &table, const QString &column )
void QgsPgSourceSelect::addSearchGeometryColumn( QgsPostgresLayerProperty layerProperty )
{
// store the column details and do the query in a thread
if ( mColumnTypeThread == NULL )
if ( !mColumnTypeThread )
{
mColumnTypeThread = new QgsGeomColumnTypeThread();
mColumnTypeThread->setConnInfo( m_connInfo, mUseEstimatedMetadata );
QgsPostgresConn *conn = QgsPostgresConn::connectDb( m_connInfo, true /* readonly */ );
if ( conn )
{
mColumnTypeThread = new QgsGeomColumnTypeThread( conn, mUseEstimatedMetadata );
}
}
mColumnTypeThread->addGeometryColumn( schema, table, column );
mColumnTypeThread->addGeometryColumn( layerProperty );
}
QString QgsPgSourceSelect::fullDescription( QString schema, QString table,
QString column, QString type )
{
QString full_desc = "";
if ( schema.length() > 0 )
full_desc = '"' + schema + "\".\"";
full_desc += table + "\" (" + column + ") " + type;
if ( !schema.isEmpty() )
full_desc = QgsPostgresConn::quotedIdentifier( schema ) + ".";
full_desc += QgsPostgresConn::quotedIdentifier( table ) + " (" + column + ") " + type;
return full_desc;
}
void QgsPgSourceSelect::setConnectionListPosition()
{
// If possible, set the item currently displayed database
QString toSelect = QgsPostgresConnection::selectedConnection();
QString toSelect = QgsPostgresConn::selectedConnection();
cmbConnections->setCurrentIndex( cmbConnections->findText( toSelect ) );
if ( cmbConnections->currentIndex() < 0 )
@ -602,112 +669,3 @@ void QgsPgSourceSelect::setSearchExpression( const QString& regexp )
{
Q_UNUSED( regexp );
}
void QgsGeomColumnTypeThread::setConnInfo( QString conninfo, bool useEstimatedMetadata )
{
mConnInfo = conninfo;
mUseEstimatedMetadata = useEstimatedMetadata;
}
void QgsGeomColumnTypeThread::addGeometryColumn( QString schema, QString table, QString column )
{
schemas.push_back( schema );
tables.push_back( table );
columns.push_back( column );
}
void QgsGeomColumnTypeThread::stop()
{
mStopped = true;
}
void QgsGeomColumnTypeThread::getLayerTypes()
{
mStopped = false;
PGconn *pd = PQconnectdb( mConnInfo.toLocal8Bit() );
// check the connection status
if ( PQstatus( pd ) != CONNECTION_OK )
{
PQfinish( pd );
QgsDataSourceURI uri( mConnInfo );
QString username = uri.username();
QString password = uri.password();
// use cached credentials
bool ok = QgsCredentials::instance()->get( mConnInfo, username, password, QString::fromUtf8( PQerrorMessage( pd ) ) );
if ( !ok )
return;
if ( !username.isEmpty() )
uri.setUsername( username );
if ( !password.isEmpty() )
uri.setPassword( password );
pd = PQconnectdb( uri.connectionInfo().toLocal8Bit() );
if ( PQstatus( pd ) == CONNECTION_OK )
QgsCredentials::instance()->put( mConnInfo, username, password );
}
if ( PQstatus( pd ) == CONNECTION_OK )
{
PQsetClientEncoding( pd, QString( "UNICODE" ).toLocal8Bit() );
PGresult *res = PQexec( pd, "SET application_name='Quantum GIS'" );
if ( !res || PQresultStatus( res ) != PGRES_COMMAND_OK )
{
PQclear( res );
res = PQexec( pd, "ROLLBACK" );
}
PQclear( res );
for ( uint i = 0; i < schemas.size() && !mStopped; i++ )
{
QString query = QString( "select distinct "
"case"
" when upper(geometrytype(%1)) IN ('POINT','MULTIPOINT') THEN 'POINT'"
" when upper(geometrytype(%1)) IN ('LINESTRING','MULTILINESTRING') THEN 'LINESTRING'"
" when upper(geometrytype(%1)) IN ('POLYGON','MULTIPOLYGON') THEN 'POLYGON'"
" end "
"from " ).arg( "\"" + columns[i] + "\"" );
if ( mUseEstimatedMetadata )
{
query += QString( "(select %1 from %2 where %1 is not null limit %3) as t" )
.arg( "\"" + columns[i] + "\"" )
.arg( "\"" + schemas[i] + "\".\"" + tables[i] + "\"" )
.arg( sGeomTypeSelectLimit );
}
else
{
query += "\"" + schemas[i] + "\".\"" + tables[i] + "\"";
}
QgsDebugMsg( "sql: " + query );
PGresult *gresult = PQexec( pd, query.toUtf8() );
QString type;
if ( PQresultStatus( gresult ) == PGRES_TUPLES_OK )
{
QStringList types;
for ( int j = 0; j < PQntuples( gresult ); j++ )
{
QString type = QString::fromUtf8( PQgetvalue( gresult, j, 0 ) );
if ( type != "" )
types += type;
}
type = types.join( "," );
}
PQclear( gresult );
// Now tell the layer list dialog box...
emit setLayerType( schemas[i], tables[i], columns[i], type );
}
}
PQfinish( pd );
}

View File

@ -16,18 +16,13 @@
***************************************************************************/
#ifndef QGSPGSOURCESELECT_H
#define QGSPGSOURCESELECT_H
#include "ui_qgsdbsourceselectbase.h"
#include "qgisgui.h"
#include "qgsdbfilterproxymodel.h"
#include "qgsdbtablemodel.h"
#include "qgspgtablemodel.h"
#include "qgscontexthelp.h"
extern "C"
{
#include <libpq-fe.h>
}
#include <QThread>
#include <QMap>
#include <QPair>
#include <QIcon>
@ -37,58 +32,19 @@ class QPushButton;
class QStringList;
class QgsGeomColumnTypeThread;
class QgisApp;
class QgsPgSourceSelect;
class QgsPgSourceSelectDelegate : public QItemDelegate
{
Q_OBJECT;
public:
QgsPgSourceSelectDelegate( QObject *parent = NULL ) : QItemDelegate( parent )
{
}
QgsPgSourceSelectDelegate( QObject *parent = NULL )
: QItemDelegate( parent )
{}
/** Used to create an editor for when the user tries to
* change the contents of a cell */
QWidget *createEditor(
QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
Q_UNUSED( option );
if ( index.column() == QgsDbTableModel::dbtmSql )
{
QLineEdit *le = new QLineEdit( parent );
le->setText( index.data( Qt::DisplayRole ).toString() );
return le;
}
if ( index.column() == QgsDbTableModel::dbtmPkCol )
{
QStringList values = index.data( Qt::UserRole + 1 ).toStringList();
if ( values.size() > 0 )
{
QComboBox *cb = new QComboBox( parent );
cb->addItems( values );
cb->setCurrentIndex( cb->findText( index.data( Qt::DisplayRole ).toString() ) );
return cb;
}
}
return NULL;
}
void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
{
QComboBox *cb = qobject_cast<QComboBox *>( editor );
if ( cb )
model->setData( index, cb->currentText() );
QLineEdit *le = qobject_cast<QLineEdit *>( editor );
if ( le )
model->setData( index, le->text() );
}
QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const;
void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const;
};
@ -108,7 +64,6 @@ class QgsPgSourceSelect : public QDialog, private Ui::QgsDbSourceSelectBase
//! static function to delete a connection
static void deleteConnection( QString key );
//! Constructor
QgsPgSourceSelect( QWidget *parent = 0, Qt::WFlags fl = QgisGui::ModalDialogFlags, bool managerMode = false, bool embeddedMode = false );
//! Destructor
@ -121,8 +76,7 @@ class QgsPgSourceSelect : public QDialog, private Ui::QgsDbSourceSelectBase
QString connectionInfo();
signals:
void addDatabaseLayers( QStringList const & layerPathList,
QString const & providerKey );
void addDatabaseLayers( QStringList const & layerPathList, QString const & providerKey );
void connectionsChanged();
public slots:
@ -151,7 +105,7 @@ class QgsPgSourceSelect : public QDialog, private Ui::QgsDbSourceSelectBase
void setSql( const QModelIndex& index );
//! Store the selected database
void on_cmbConnections_activated( int );
void setLayerType( QString schema, QString table, QString column, QString type );
void setLayerType( QgsPostgresLayerProperty layerProperty );
void on_mTablesTreeView_clicked( const QModelIndex &index );
void on_mTablesTreeView_doubleClicked( const QModelIndex &index );
//!Sets a new regular expression to the model
@ -159,6 +113,8 @@ class QgsPgSourceSelect : public QDialog, private Ui::QgsDbSourceSelectBase
void on_buttonBox_helpRequested() { QgsContextHelp::run( metaObject()->className() ); }
void columnThreadFinished();
private:
typedef QPair<QString, QString> geomPair;
typedef QList<geomPair> geomCol;
@ -170,7 +126,7 @@ class QgsPgSourceSelect : public QDialog, private Ui::QgsDbSourceSelectBase
bool mEmbeddedMode;
// queue another query for the thread
void addSearchGeometryColumn( const QString &schema, const QString &table, const QString &column );
void addSearchGeometryColumn( QgsPostgresLayerProperty layerProperty );
// Set the position of the database connection list to the last
// used one.
@ -189,50 +145,13 @@ class QgsPgSourceSelect : public QDialog, private Ui::QgsDbSourceSelectBase
QMap<QString, QPair<QString, QIcon> > mLayerIcons;
//! Model that acts as datasource for mTableTreeWidget
QgsDbTableModel mTableModel;
QgsPgTableModel mTableModel;
QgsDbFilterProxyModel mProxyModel;
QString layerURI( const QModelIndex &index );
QPushButton *mBuildQueryButton;
QPushButton *mAddButton;
};
// Perhaps this class should be in its own file??
//
// A class that determines the geometry type of a given database
// schema.table.column, with the option of doing so in a separate
// thread.
class QgsGeomColumnTypeThread : public QThread
{
Q_OBJECT
public:
void setConnInfo( QString s, bool useEstimatedMetadata );
void addGeometryColumn( QString schema, QString table, QString column );
// These functions get the layer types and pass that information out
// by emitting the setLayerType() signal. The getLayerTypes()
// function does the actual work, but use the run() function if you
// want the work to be done as a separate thread from the calling
// process.
virtual void run() { getLayerTypes(); }
void getLayerTypes();
signals:
void setLayerType( QString schema, QString table, QString column,
QString type );
public slots:
void stop();
private:
QString mConnInfo;
bool mUseEstimatedMetadata;
bool mStopped;
std::vector<QString> schemas, tables, columns;
void updateSelectableState( const QModelIndex &index );
};
#endif // QGSPGSOURCESELECT_H

View File

@ -1,5 +1,5 @@
/***************************************************************************
qgsdbtablemodel.cpp - description
qgspgtablemodel.cpp - description
-------------------
begin : Dec 2007
copyright : (C) 2007 by Marco Hugentobler
@ -15,90 +15,119 @@
* *
***************************************************************************/
#include "qgsdbtablemodel.h"
#include "qgspgtablemodel.h"
#include "qgsdataitem.h"
#include "qgslogger.h"
QgsDbTableModel::QgsDbTableModel(): QStandardItemModel(), mTableCount( 0 )
QgsPgTableModel::QgsPgTableModel()
: QStandardItemModel()
, mTableCount( 0 )
{
QStringList headerLabels;
headerLabels << tr( "Schema" );
headerLabels << tr( "Table" );
headerLabels << tr( "Type" );
headerLabels << tr( "Geometry column" );
headerLabels << tr( "SRID" );
headerLabels << tr( "Primary key column" );
headerLabels << tr( "Select at id" );
headerLabels << tr( "Sql" );
setHorizontalHeaderLabels( headerLabels );
}
QgsDbTableModel::~QgsDbTableModel()
QgsPgTableModel::~QgsPgTableModel()
{
}
void QgsDbTableModel::addTableEntry( QString type, QString schemaName, QString tableName, QString geometryColName, const QStringList &pkCols, QString sql )
void QgsPgTableModel::addTableEntry( QgsPostgresLayerProperty layerProperty )
{
//is there already a root item with the given scheme Name?
QStandardItem *schemaItem;
QList<QStandardItem*> schemaItems = findItems( schemaName, Qt::MatchExactly, dbtmSchema );
QgsDebugMsg( QString( "%1.%2.%3 type=%4 srid=%5 pk=%6 sql=%7" )
.arg( layerProperty.schemaName )
.arg( layerProperty.tableName )
.arg( layerProperty.geometryColName )
.arg( layerProperty.type )
.arg( layerProperty.srid )
.arg( layerProperty.pkCols.join( "," ) )
.arg( layerProperty.sql ) );
//there is already an item for this schema
// is there already a root item with the given scheme Name?
QStandardItem *schemaItem;
QList<QStandardItem*> schemaItems = findItems( layerProperty.schemaName, Qt::MatchExactly, dbtmSchema );
// there is already an item for this schema
if ( schemaItems.size() > 0 )
{
schemaItem = schemaItems.at( dbtmSchema );
}
else //create a new toplevel item for this schema
else
{
schemaItem = new QStandardItem( schemaName );
// create a new toplevel item for this schema
schemaItem = new QStandardItem( layerProperty.schemaName );
schemaItem->setFlags( Qt::ItemIsEnabled );
invisibleRootItem()->setChild( invisibleRootItem()->rowCount(), schemaItem );
}
//path to icon for specified type
QString typeName;
QGis::WkbType wkbType = qgisTypeFromDbType( type );
QIcon iconFile = iconForType( wkbType );
QGis::WkbType wkbType = qgisTypeFromDbType( layerProperty.type );
if ( wkbType == QGis::WKBUnknown && layerProperty.geometryColName.isEmpty() )
{
wkbType = QGis::WKBNoGeometry;
}
QList<QStandardItem*> childItemList;
QStandardItem* schemaNameItem = new QStandardItem( schemaName );
QStandardItem *schemaNameItem = new QStandardItem( layerProperty.schemaName );
schemaNameItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
QStandardItem* typeItem = new QStandardItem( QIcon( iconFile ), type );
typeItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
QStandardItem *typeItem = new QStandardItem( iconForType( wkbType ), wkbType == QGis::WKBUnknown ? tr( "Waiting..." ) : displayStringForType( wkbType ) );
typeItem->setData( wkbType == QGis::WKBUnknown, Qt::UserRole + 1 );
typeItem->setData( wkbType, Qt::UserRole + 2 );
typeItem->setFlags(( wkbType != QGis::WKBUnknown ? Qt::ItemIsEnabled : Qt::NoItemFlags ) | Qt::ItemIsSelectable | Qt::ItemIsEditable );
QStandardItem* tableItem = new QStandardItem( tableName );
QStandardItem *tableItem = new QStandardItem( layerProperty.tableName );
tableItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
QStandardItem* geomItem = new QStandardItem( geometryColName );
QStandardItem *geomItem = new QStandardItem( layerProperty.geometryColName );
geomItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
QStandardItem* pkItem = new QStandardItem( "" );
pkItem->setData( pkCols );
QStandardItem *sridItem = new QStandardItem( QString::number( layerProperty.srid ) );
sridItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
QString pkText, pkCol = "";
switch ( layerProperty.pkCols.size() )
{
case 0: pkText = ""; break;
case 1: pkText = layerProperty.pkCols[0]; pkCol = pkText; break;
default: pkText = tr( "Select..." ); break;
}
QStandardItem *pkItem = new QStandardItem( pkText );
pkItem->setData( layerProperty.pkCols, Qt::UserRole + 1 );
pkItem->setData( pkCol, Qt::UserRole + 2 );
pkItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
QStandardItem* selItem = new QStandardItem( "" );
QStandardItem *selItem = new QStandardItem( "" );
selItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable );
selItem->setCheckState( Qt::Checked );
selItem->setToolTip( tr( "Disable 'Fast Access to Features at ID' capability to force keeping the attribute table in memory (e.g. in case of expensive views)." ) );
QStandardItem* sqlItem = new QStandardItem( sql );
QStandardItem* sqlItem = new QStandardItem( layerProperty.sql );
sqlItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
childItemList.push_back( schemaNameItem );
childItemList.push_back( tableItem );
childItemList.push_back( typeItem );
childItemList.push_back( geomItem );
childItemList.push_back( pkItem );
childItemList.push_back( selItem );
childItemList.push_back( sqlItem );
childItemList << schemaNameItem;
childItemList << tableItem;
childItemList << typeItem;
childItemList << geomItem;
childItemList << sridItem;
childItemList << pkItem;
childItemList << selItem;
childItemList << sqlItem;
schemaItem->appendRow( childItemList );
++mTableCount;
}
void QgsDbTableModel::setSql( const QModelIndex &index, const QString &sql )
void QgsPgTableModel::setSql( const QModelIndex &index, const QString &sql )
{
if ( !index.isValid() || !index.parent().isValid() )
{
@ -126,26 +155,22 @@ void QgsDbTableModel::setSql( const QModelIndex &index, const QString &sql )
}
QStandardItem* schemaItem = schemaItems.at( dbtmSchema );
int numChildren = schemaItem->rowCount();
QModelIndex currentChildIndex;
QModelIndex currentTableIndex;
QModelIndex currentGeomIndex;
for ( int i = 0; i < numChildren; ++i )
int n = schemaItem->rowCount();
for ( int i = 0; i < n; i++ )
{
currentChildIndex = indexFromItem( schemaItem->child( i, dbtmSchema ) );
QModelIndex currentChildIndex = indexFromItem( schemaItem->child( i, dbtmSchema ) );
if ( !currentChildIndex.isValid() )
{
continue;
}
currentTableIndex = currentChildIndex.sibling( i, dbtmTable );
QModelIndex currentTableIndex = currentChildIndex.sibling( i, dbtmTable );
if ( !currentTableIndex.isValid() )
{
continue;
}
currentGeomIndex = currentChildIndex.sibling( i, dbtmGeomCol );
QModelIndex currentGeomIndex = currentChildIndex.sibling( i, dbtmGeomCol );
if ( !currentGeomIndex.isValid() )
{
continue;
@ -164,75 +189,82 @@ void QgsDbTableModel::setSql( const QModelIndex &index, const QString &sql )
}
}
void QgsDbTableModel::setGeometryTypesForTable( const QString& schema, const QString& table, const QString& attribute, const QString& type )
void QgsPgTableModel::setGeometryTypesForTable( QgsPostgresLayerProperty layerProperty )
{
bool typeIsEmpty = type.isEmpty(); //true means the table has no valid geometry entry and the item for this table should be removed
QStringList typeList = type.split( "," );
QStringList typeList = layerProperty.type.split( ",", QString::SkipEmptyParts );
//find schema item and table item
QStandardItem* schemaItem;
QList<QStandardItem*> schemaItems = findItems( schema, Qt::MatchExactly, dbtmSchema );
QList<QStandardItem*> schemaItems = findItems( layerProperty.schemaName, Qt::MatchExactly, dbtmSchema );
if ( schemaItems.size() < 1 )
{
return;
}
schemaItem = schemaItems.at( 0 );
int numChildren = schemaItem->rowCount();
QModelIndex currentChildIndex;
QModelIndex currentTableIndex;
QModelIndex currentTypeIndex;
QModelIndex currentGeomColumnIndex;
QModelIndex currentPkColumnIndex;
for ( int i = 0; i < numChildren; ++i )
int n = schemaItem->rowCount();
for ( int i = 0; i < n; i++ )
{
currentChildIndex = indexFromItem( schemaItem->child( i, dbtmSchema ) );
QModelIndex currentChildIndex = indexFromItem( schemaItem->child( i, dbtmSchema ) );
if ( !currentChildIndex.isValid() )
{
continue;
}
currentTableIndex = currentChildIndex.sibling( i, dbtmTable );
currentTypeIndex = currentChildIndex.sibling( i, dbtmType );
currentGeomColumnIndex = currentChildIndex.sibling( i, dbtmGeomCol );
currentPkColumnIndex = currentChildIndex.sibling( i, dbtmPkCol );
QString geomColText = itemFromIndex( currentGeomColumnIndex )->text();
QStringList pkCols = itemFromIndex( currentPkColumnIndex )->data().toStringList();
if ( !currentTypeIndex.isValid() || !currentTableIndex.isValid() || !currentGeomColumnIndex.isValid() )
QModelIndex currentTypeIndex = currentChildIndex.sibling( i, dbtmType );
QModelIndex currentTableIndex = currentChildIndex.sibling( i, dbtmTable );
QModelIndex currentGeomColumnIndex = currentChildIndex.sibling( i, dbtmGeomCol );
QModelIndex currentPkColumnIndex = currentChildIndex.sibling( i, dbtmPkCol );
QModelIndex currentSridIndex = currentChildIndex.sibling( i, dbtmSrid );
if ( !currentTypeIndex.isValid()
|| !currentTableIndex.isValid()
|| !currentGeomColumnIndex.isValid()
|| !currentPkColumnIndex.isValid()
|| !currentSridIndex.isValid()
)
{
continue;
}
if ( itemFromIndex( currentTableIndex )->text() == table &&
( geomColText == attribute || geomColText.startsWith( attribute + " AS " ) ) )
QString tableText = itemFromIndex( currentTableIndex )->text();
QString geomColText = itemFromIndex( currentGeomColumnIndex )->text();
QStandardItem *typeItem = itemFromIndex( currentTypeIndex );
QStandardItem *sridItem = itemFromIndex( currentSridIndex );
if ( tableText == layerProperty.tableName && geomColText == layerProperty.geometryColName )
{
if ( typeIsEmpty )
sridItem->setText( QString::number( layerProperty.srid ) );
if ( typeList.isEmpty() )
{
removeRow( i, indexFromItem( schemaItem ) );
return;
typeItem->setText( tr( "Select..." ) );
}
else
{
// update existing row
QGis::WkbType wkbType = qgisTypeFromDbType( typeList.at( 0 ) );
typeItem->setIcon( iconForType( wkbType ) );
typeItem->setText( displayStringForType( wkbType ) );
typeItem->setData( false, Qt::UserRole + 1 );
typeItem->setData( wkbType, Qt::UserRole + 2 );
for ( int j = 1; j < typeList.size(); j++ )
{
layerProperty.type = typeList[j];
addTableEntry( layerProperty );
}
}
QGis::WkbType wkbType = qgisTypeFromDbType( typeList.at( 0 ) );
QIcon myIcon = iconForType( wkbType );
itemFromIndex( currentTypeIndex )->setText( typeList.at( 0 ) ); //todo: add other rows
itemFromIndex( currentTypeIndex )->setIcon( myIcon );
if ( !geomColText.contains( " AS " ) )
{
itemFromIndex( currentGeomColumnIndex )->setText( geomColText + " AS " + typeList.at( 0 ) );
}
for ( int j = 1; j < typeList.size(); ++j )
{
//todo: add correct type
addTableEntry( typeList.at( j ), schema, table, geomColText + " AS " + typeList.at( j ), pkCols, "" );
}
typeItem->setFlags( typeItem->flags() | Qt::ItemIsEnabled );
}
}
}
QIcon QgsDbTableModel::iconForType( QGis::WkbType type ) const
QIcon QgsPgTableModel::iconForType( QGis::WkbType type )
{
if ( type == QGis::WKBPoint || type == QGis::WKBPoint25D || type == QGis::WKBMultiPoint || type == QGis::WKBMultiPoint25D )
{
@ -246,10 +278,17 @@ QIcon QgsDbTableModel::iconForType( QGis::WkbType type ) const
{
return QIcon( QgsDataItem::getThemePixmap( "/mIconPolygonLayer.png" ) );
}
else return QIcon();
else if ( type == QGis::WKBNoGeometry )
{
return QIcon( QgsDataItem::getThemePixmap( "/mIconTableLayer.png" ) );
}
else
{
return QIcon( QgsDataItem::getThemePixmap( "/mIconLayer.png" ) );
}
}
QString QgsDbTableModel::displayStringForType( QGis::WkbType type ) const
QString QgsPgTableModel::displayStringForType( QGis::WkbType type )
{
if ( type == QGis::WKBPoint || type == QGis::WKBPoint25D )
{
@ -275,11 +314,20 @@ QString QgsDbTableModel::displayStringForType( QGis::WkbType type ) const
{
return tr( "Multipolygon" );
}
return "Unknown";
else if ( type == QGis::WKBNoGeometry )
{
return tr( "No Geometry" );
}
else
{
return tr( "Unknown" );
}
}
QGis::WkbType QgsDbTableModel::qgisTypeFromDbType( const QString& dbType ) const
QGis::WkbType QgsPgTableModel::qgisTypeFromDbType( QString dbType )
{
dbType = dbType.toUpper();
if ( dbType == "POINT" )
{
return QGis::WKBPoint;
@ -304,5 +352,8 @@ QGis::WkbType QgsDbTableModel::qgisTypeFromDbType( const QString& dbType ) const
{
return QGis::WKBMultiPolygon;
}
return QGis::WKBUnknown;
else
{
return QGis::WKBUnknown;
}
}

View File

@ -1,5 +1,5 @@
/***************************************************************************
qgsdbtablemodel.h - description
qgspgtablemodel.h - description
-------------------
begin : Dec 2007
copyright : (C) 2007 by Marco Hugentobler
@ -16,28 +16,31 @@
***************************************************************************/
#include <QStandardItemModel>
class QIcon;
#include "qgis.h"
#include "qgspostgresconn.h"
class QIcon;
/**A model that holds the tables of a database in a hierarchy where the
schemas are the root elements that contain the individual tables as children.
The tables have the following columns: Type, Schema, Tablename, Geometry Column, Sql*/
class CORE_EXPORT QgsDbTableModel : public QStandardItemModel
class QgsPgTableModel : public QStandardItemModel
{
Q_OBJECT
public:
QgsDbTableModel();
~QgsDbTableModel();
QgsPgTableModel();
~QgsPgTableModel();
/**Adds entry for one database table to the model*/
void addTableEntry( QString type, QString schemaName, QString tableName, QString geometryColName, const QStringList &pkCols, QString Sql );
void addTableEntry( QgsPostgresLayerProperty property );
/**Sets an sql statement that belongs to a cell specified by a model index*/
void setSql( const QModelIndex& index, const QString& sql );
/**Sets one or more geometry types to a row. In case of several types, additional rows are inserted.
This is for tables where the type is dectected later by thread*/
void setGeometryTypesForTable( const QString& schema, const QString& table, const QString& attribute, const QString& type );
void setGeometryTypesForTable( QgsPostgresLayerProperty layerProperty );
/**Returns the number of tables in the model*/
int tableCount() const { return mTableCount; }
@ -48,20 +51,19 @@ class CORE_EXPORT QgsDbTableModel : public QStandardItemModel
dbtmTable,
dbtmType,
dbtmGeomCol,
dbtmSrid,
dbtmPkCol,
dbtmSelectAtId,
dbtmSql,
dbtmColumns
};
static QIcon iconForType( QGis::WkbType type );
static QString displayStringForType( QGis::WkbType type );
static QGis::WkbType qgisTypeFromDbType( QString dbType );
private:
/**Number of tables in the model*/
int mTableCount;
QIcon iconForType( QGis::WkbType type ) const;
QString displayStringForType( QGis::WkbType type ) const;
/**Returns qgis wkbtype from database typename*/
QGis::WkbType qgisTypeFromDbType( const QString& dbType ) const;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,236 @@
/***************************************************************************
qgspostgresconn.h - connection class to PostgreSQL/PostGIS
-------------------
begin : 2011/01/28
copyright : (C) 2011 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 QGSPOSTGRESCONN_H
#define QGSPOSTGRESCONN_H
#include <QString>
#include <QStringList>
#include <QVector>
#include <QMap>
#include <QMutex>
#include "qgis.h"
#include "qgsdatasourceuri.h"
extern "C"
{
#include <libpq-fe.h>
}
class QgsField;
/** Layer Property structure */
// TODO: Fill to Postgres/PostGIS specifications
struct QgsPostgresLayerProperty
{
// Postgres/PostGIS layer properties
QString type;
QString schemaName;
QString tableName;
QString geometryColName;
QStringList pkCols;
int srid;
QString sql;
};
class QgsPostgresResult
{
public:
QgsPostgresResult( PGresult *theRes = 0 ) : mRes( theRes ) {}
~QgsPostgresResult();
QgsPostgresResult &operator=( PGresult *theRes );
QgsPostgresResult &operator=( const QgsPostgresResult &src );
ExecStatusType PQresultStatus();
QString PQresultErrorMessage();
int PQntuples();
QString PQgetvalue( int row, int col );
bool PQgetisnull( int row, int col );
int PQnfields();
QString PQfname( int col );
int PQftable( int col );
int PQftype( int col );
int PQftablecol( int col );
PGresult *result() const { return mRes; }
private:
PGresult *mRes;
};
class QgsPostgresConn : public QObject
{
Q_OBJECT;
public:
static QgsPostgresConn *connectDb( QString connInfo, bool readOnly );
void disconnect();
//! get postgis version string
QString postgisVersion();
//! get status of GEOS capability
bool hasGEOS();
//! get status of topology capability
bool hasTopology();
//! get status of GIST capability
bool hasGIST();
//! get status of PROJ4 capability
bool hasPROJ();
//! encode wkb in hex
bool useWkbHex() { return mUseWkbHex; }
//! major PostgreSQL version
int majorVersion() { return mPostgisVersionMajor; }
//! PostgreSQL version
int pgVersion() { return mPostgresqlVersion; }
//! run a query and free result buffer
bool PQexecNR( QString query, bool retry = true );
//! cursor handling
bool openCursor( QString cursorName, QString declare );
bool closeCursor( QString cursorName );
#if 0
PGconn *pgConnection() { return mConn; }
#endif
//
// libpq wrapper
//
// run a query and check for errors
PGresult *PQexec( QString query );
void PQfinish();
QString PQerrorMessage();
int PQsendQuery( QString query );
int PQstatus();
PGresult *PQgetResult();
PGresult *PQprepare( QString stmtName, QString query, int nParams, const Oid *paramTypes );
PGresult *PQexecPrepared( QString stmtName, const QStringList &params );
/** Double quote a PostgreSQL identifier for placement in a SQL string.
*/
static QString quotedIdentifier( QString ident );
/** Quote a value for placement in a SQL string.
*/
static QString quotedValue( QVariant value );
//! Get the list of supported layers
bool supportedLayers( QVector<QgsPostgresLayerProperty> &layers,
bool searchGeometryColumnsOnly = true,
bool searchPublicOnly = true,
bool allowGeometrylessTables = false );
void retrieveLayerTypes( QgsPostgresLayerProperty &layerProperty, bool useEstimatedMetadata );
/** Gets information about the spatial tables */
bool getTableInfo( bool searchGeometryColumnsOnly, bool searchPublicOnly, bool allowGeometrylessTables );
/** get primary key candidates (all int4 columns) */
QStringList pkCandidates( QString schemaName, QString viewName );
qint64 getBinaryInt( QgsPostgresResult &queryResult, int row, int col );
QString fieldExpression( const QgsField &fld );
QString connInfo() const { return mConnInfo; }
static const int sGeomTypeSelectLimit;
static QString postgisGeometryTypeName( QGis::WkbType wkbType );
static int postgisGeometryTypeDim( QGis::WkbType wkbType );
static void postgisGeometryType( QGis::WkbType wkbType, QString &geometryType, int &dim );
static QGis::WkbType wkbTypeFromPostgis( QString type );
static QStringList connectionList();
static QString selectedConnection();
static void setSelectedConnection( QString theConnName );
static QgsDataSourceURI connUri( QString theConnName );
private:
QgsPostgresConn( QString conninfo, bool readOnly );
~QgsPostgresConn();
int mRef;
int mOpenCursors;
PGconn *mConn;
QString mConnInfo;
//! GEOS capability
bool mGeosAvailable;
//! Topology capability
bool mTopologyAvailable;
//! PostGIS version string
QString mPostgisVersionInfo;
//! Are mPostgisVersionMajor, mPostgisVersionMinor, mGeosAvailable, mGistAvailable, mProjAvailable, mTopologyAvailable valid?
bool mGotPostgisVersion;
//! PostgreSQL version
int mPostgresqlVersion;
//! PostGIS major version
int mPostgisVersionMajor;
//! PostGIS minor version
int mPostgisVersionMinor;
//! GIST capability
bool mGistAvailable;
//! PROJ4 capability
bool mProjAvailable;
//! encode wkb in hex
bool mUseWkbHex;
bool mReadOnly;
static QMap<QString, QgsPostgresConn *> sConnectionsRW;
static QMap<QString, QgsPostgresConn *> sConnectionsRO;
//! List of the supported layers
QVector<QgsPostgresLayerProperty> mLayersSupported;
/**
* Flag indicating whether data from binary cursors must undergo an
* endian conversion prior to use
@note
XXX Umm, it'd be helpful to know what we're swapping from and to.
XXX Presumably this means swapping from big-endian (network) byte order
XXX to little-endian; but the inverse transaction is possible, too, and
XXX that's not reflected in this variable
*/
bool mSwapEndian;
void deduceEndian();
};
#endif

View File

@ -1,132 +0,0 @@
/***************************************************************************
qgspostgresconnection.cpp - PostgreSQL/PostGIS connection
-------------------
begin : 3 June 2011
copyright : (C) 2011 by Giuseppe Sucameli
email : brush.tyler at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* 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 "qgspostgresconnection.h"
#include <qgslogger.h>
#include "qgspostgresprovider.h"
#include "qgsproviderregistry.h"
#include "qgsdatasourceuri.h"
#include <QSettings>
QStringList QgsPostgresConnection::connectionList()
{
QSettings settings;
settings.beginGroup( "/PostgreSQL/connections" );
return settings.childGroups();
}
QString QgsPostgresConnection::selectedConnection()
{
QSettings settings;
return settings.value( "/PostgreSQL/connections/selected" ).toString();
}
void QgsPostgresConnection::setSelectedConnection( QString name )
{
QSettings settings;
return settings.setValue( "/PostgreSQL/connections/selected", name );
}
QgsPostgresConnection::QgsPostgresConnection( QString theConnName ) :
mConnName( theConnName )
{
QgsDebugMsg( "theConnName = " + theConnName );
QSettings settings;
QString key = "/PostgreSQL/connections/" + mConnName;
QString service = settings.value( key + "/service" ).toString();
QString host = settings.value( key + "/host" ).toString();
QString port = settings.value( key + "/port" ).toString();
if ( port.length() == 0 )
{
port = "5432";
}
QString database = settings.value( key + "/database" ).toString();
//bool publicSchemaOnly = settings.value( key + "/publicOnly", false ).toBool();
//bool geometryColumnsOnly = settings.value( key + "/geometrycolumnsOnly", false ).toBool();
//bool allowGeometrylessTables = settings.value( key + "/allowGeometrylessTables", false ).toBool();
bool useEstimatedMetadata = settings.value( key + "/estimatedMetadata", false ).toBool();
int sslmode = settings.value( key + "/sslmode", QgsDataSourceURI::SSLprefer ).toInt();
QString username;
QString password;
if ( settings.value( key + "/saveUsername" ).toString() == "true" )
{
username = settings.value( key + "/username" ).toString();
}
if ( settings.value( key + "/savePassword" ).toString() == "true" )
{
password = settings.value( key + "/password" ).toString();
}
// Old save setting
if ( settings.contains( key + "/save" ) )
{
username = settings.value( key + "/username" ).toString();
if ( settings.value( key + "/save" ).toString() == "true" )
{
password = settings.value( key + "/password" ).toString();
}
}
QgsDataSourceURI uri;
if ( !service.isEmpty() )
{
uri.setConnection( service, database, username, password, ( QgsDataSourceURI::SSLmode ) sslmode );
}
else
{
uri.setConnection( host, port, database, username, password, ( QgsDataSourceURI::SSLmode ) sslmode );
}
uri.setUseEstimatedMetadata( useEstimatedMetadata );
mConnectionInfo = uri.uri();
QgsDebugMsg( QString( "Connection info: '%1'." ).arg( mConnectionInfo ) );
}
QgsPostgresConnection::~QgsPostgresConnection()
{
}
QString QgsPostgresConnection::connectionInfo( )
{
return mConnectionInfo;
}
QgsPostgresProvider * QgsPostgresConnection::provider( )
{
// TODO: Create and bind to data provider
// load the server data provider plugin
QgsProviderRegistry * pReg = QgsProviderRegistry::instance();
QgsPostgresProvider *postgresProvider =
( QgsPostgresProvider* ) pReg->provider( "postgres", mConnectionInfo );
return postgresProvider;
}

View File

@ -1,51 +0,0 @@
/***************************************************************************
qgspostgresconnection.h - PostgreSQL/PostGIS connection
-------------------
begin : 3 June 2011
copyright : (C) 2011 by Giuseppe Sucameli
email : brush.tyler at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* 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 QGSPOSTGRESCONNECTION_H
#define QGSPOSTGRESCONNECTION_H
#include <QStringList>
class QgsPostgresProvider;
/*!
* \brief Connections management
*/
class QgsPostgresConnection : public QObject
{
Q_OBJECT
public:
//! Constructor
QgsPostgresConnection( QString theConnName );
//! Destructor
~QgsPostgresConnection();
static QStringList connectionList();
static QString selectedConnection();
static void setSelectedConnection( QString name );
public:
QgsPostgresProvider *provider();
QString connectionInfo();
QString mConnName;
QString mConnectionInfo;
};
#endif // QGSPOSTGRESCONNECTION_H

View File

@ -1,10 +1,10 @@
#include "qgspostgresdataitems.h"
#include "qgslogger.h"
#include "qgspostgresconnection.h"
#include "qgspgsourceselect.h"
#include "qgspgnewconnection.h"
#include "qgscolumntypethread.h"
#include "qgslogger.h"
#include "qgsdatasourceuri.h"
// ---------------------------------------------------------------------------
QgsPGConnectionItem::QgsPGConnectionItem( QgsDataItem* parent, QString name, QString path )
@ -21,47 +21,87 @@ QVector<QgsDataItem*> QgsPGConnectionItem::createChildren()
{
QgsDebugMsg( "Entered" );
QVector<QgsDataItem*> children;
QgsPostgresConnection connection( mName );
QgsPostgresProvider *pgProvider = connection.provider( );
if ( !pgProvider )
QgsDataSourceURI uri = QgsPostgresConn::connUri( mName );
mConn = QgsPostgresConn::connectDb( uri.connectionInfo(), true );
if ( !mConn )
return children;
QString mConnInfo = connection.connectionInfo();
QgsDebugMsg( "mConnInfo = " + mConnInfo );
QVector<QgsPostgresLayerProperty> layerProperties;
if ( !pgProvider->supportedLayers( layerProperties, true, false, false ) )
if ( !mConn->supportedLayers( layerProperties, true, false, false ) )
{
children.append( new QgsErrorItem( this, tr( "Failed to retrieve layers" ), mPath + "/error" ) );
return children;
}
// fill the schemas map
mSchemasMap.clear();
QgsGeomColumnTypeThread *columnTypeThread = 0;
foreach( QgsPostgresLayerProperty layerProperty, layerProperties )
{
mSchemasMap[ layerProperty.schemaName ].push_back( layerProperty );
QgsPGSchemaItem *schemaItem = mSchemaMap.value( layerProperty.schemaName, 0 );
if ( !schemaItem )
{
schemaItem = new QgsPGSchemaItem( this, layerProperty.schemaName, mPath + "/" + layerProperty.schemaName );
children.append( schemaItem );
mSchemaMap[ layerProperty.schemaName ] = schemaItem;
}
if ( layerProperty.type == "GEOMETRY" )
{
if ( !columnTypeThread )
{
QgsPostgresConn *conn = QgsPostgresConn::connectDb( uri.connectionInfo(), true /* readonly */ );
if ( conn )
{
columnTypeThread = new QgsGeomColumnTypeThread( conn, true /* use estimated metadata */ );
connect( columnTypeThread, SIGNAL( setLayerType( QgsPostgresLayerProperty ) ),
this, SLOT( setLayerType( QgsPostgresLayerProperty ) ) );
columnTypeThread->start();
}
}
columnTypeThread->addGeometryColumn( layerProperty );
continue;
}
schemaItem->addLayer( layerProperty );
}
QMap<QString, QVector<QgsPostgresLayerProperty> >::const_iterator it = mSchemasMap.constBegin();
for ( ; it != mSchemasMap.constEnd(); it++ )
{
QgsDebugMsg( "schema: " + it.key() );
QgsPGSchemaItem * schema = new QgsPGSchemaItem( this, it.key(), mPath + "/" + it.key(), mConnInfo );
children.append( schema );
}
return children;
}
void QgsPGConnectionItem::setLayerType( QgsPostgresLayerProperty layerProperty )
{
QgsPGSchemaItem *schemaItem = mSchemaMap.value( layerProperty.schemaName, 0 );
if ( !schemaItem )
{
QgsDebugMsg( QString( "schema item for %1 not found." ).arg( layerProperty.schemaName ) );
return;
}
foreach( QString type, layerProperty.type.split( ",", QString::SkipEmptyParts ) )
{
QGis::WkbType wkbType = QgsPgTableModel::qgisTypeFromDbType( type );
if ( wkbType == QGis::WKBUnknown )
{
QgsDebugMsg( QString( "unsupported geometry type:%1" ).arg( type ) );
continue;
}
schemaItem->addLayer( layerProperty );
}
}
bool QgsPGConnectionItem::equal( const QgsDataItem *other )
{
if ( type() != other->type() )
{
return false;
}
const QgsPGConnectionItem *o = dynamic_cast<const QgsPGConnectionItem *>( other );
return ( mPath == o->mPath && mName == o->mName && mConnInfo == o->mConnInfo );
const QgsPGConnectionItem *o = qobject_cast<const QgsPGConnectionItem *>( other );
return ( mPath == o->mPath && mName == o->mName && o->connection() == connection() );
}
QList<QAction*> QgsPGConnectionItem::actions()
@ -98,10 +138,9 @@ void QgsPGConnectionItem::deleteConnection()
// ---------------------------------------------------------------------------
QgsPGLayerItem::QgsPGLayerItem( QgsDataItem* parent, QString name, QString path, QString connInfo, QgsLayerItem::LayerType layerType, QgsPostgresLayerProperty layerProperty )
: QgsLayerItem( parent, name, path, QString(), layerType, "postgres" ),
mConnInfo( connInfo ),
mLayerProperty( layerProperty )
QgsPGLayerItem::QgsPGLayerItem( QgsDataItem* parent, QString name, QString path, QgsLayerItem::LayerType layerType, QgsPostgresLayerProperty layerProperty )
: QgsLayerItem( parent, name, path, QString(), layerType, "postgres" )
, mLayerProperty( layerProperty )
{
mUri = createUri();
mPopulated = true;
@ -114,63 +153,75 @@ QgsPGLayerItem::~QgsPGLayerItem()
QString QgsPGLayerItem::createUri()
{
QString pkColName = mLayerProperty.pkCols.size() > 0 ? mLayerProperty.pkCols.at( 0 ) : QString::null;
QgsDataSourceURI uri( mConnInfo );
QgsPGConnectionItem *connItem = qobject_cast<QgsPGConnectionItem *>( parent() ? parent()->parent() : 0 );
if ( !connItem )
{
QgsDebugMsg( "connection item not found." );
return QString::null;
}
QgsDebugMsg( QString( "connInfo: %1" ).arg( connItem->connection()->connInfo() ) );
QgsDataSourceURI uri( connItem->connection()->connInfo() );
uri.setDataSource( mLayerProperty.schemaName, mLayerProperty.tableName, mLayerProperty.geometryColName, mLayerProperty.sql, pkColName );
uri.setSrid( QString::number( mLayerProperty.srid ) );
uri.setGeometryType( QgsPgTableModel::qgisTypeFromDbType( mLayerProperty.type ) );
QgsDebugMsg( QString( "layer uri: %1" ).arg( uri.uri() ) );
return uri.uri();
}
// ---------------------------------------------------------------------------
QgsPGSchemaItem::QgsPGSchemaItem( QgsDataItem* parent, QString name, QString path, QString connInfo )
QgsPGSchemaItem::QgsPGSchemaItem( QgsDataItem* parent, QString name, QString path )
: QgsDataCollectionItem( parent, name, path )
{
mIcon = QIcon( getThemePixmap( "mIconDbSchema.png" ) );
mConnInfo = connInfo;
}
QVector<QgsDataItem*> QgsPGSchemaItem::createChildren()
{
QgsPGConnectionItem* connItem = dynamic_cast<QgsPGConnectionItem*>( mParent );
Q_ASSERT( connItem );
QVector<QgsPostgresLayerProperty> layers = connItem->mSchemasMap.value( mName );
QVector<QgsDataItem*> children;
// Populate everything, it costs nothing, all info about layers is collected
foreach( QgsPostgresLayerProperty layerProperty, layers )
{
QgsDebugMsg( "table: " + layerProperty.schemaName + "." + layerProperty.tableName );
QgsLayerItem::LayerType layerType = QgsLayerItem::NoType;
if ( layerProperty.type.contains( "POINT" ) )
{
layerType = QgsLayerItem::Point;
}
else if ( layerProperty.type.contains( "LINE" ) )
{
layerType = QgsLayerItem::Line;
}
else if ( layerProperty.type.contains( "POLYGON" ) )
{
layerType = QgsLayerItem::Polygon;
}
else if ( layerProperty.type == QString::null )
{
if ( layerProperty.geometryColName == QString::null )
{
layerType = QgsLayerItem::TableLayer;
}
}
QgsPGLayerItem * layer = new QgsPGLayerItem( this, layerProperty.tableName, mPath + "/" + layerProperty.tableName, mConnInfo, layerType, layerProperty );
children.append( layer );
}
return children;
QgsDebugMsg( "Entering." );
return QVector<QgsDataItem*>();
}
QgsPGSchemaItem::~QgsPGSchemaItem()
{
}
void QgsPGSchemaItem::addLayer( QgsPostgresLayerProperty layerProperty )
{
QGis::WkbType wkbType = QgsPgTableModel::qgisTypeFromDbType( layerProperty.type );
QString name = layerProperty.tableName + "." + layerProperty.geometryColName;
QgsLayerItem::LayerType layerType;
if ( layerProperty.type.contains( "POINT" ) )
{
layerType = QgsLayerItem::Point;
}
else if ( layerProperty.type.contains( "LINE" ) )
{
layerType = QgsLayerItem::Line;
}
else if ( layerProperty.type.contains( "POLYGON" ) )
{
layerType = QgsLayerItem::Polygon;
}
else if ( layerProperty.type.isEmpty() && layerProperty.geometryColName.isEmpty() )
{
layerType = QgsLayerItem::TableLayer;
name = layerProperty.tableName;
}
else
{
return;
}
name += " (" + QgsPgTableModel::displayStringForType( wkbType ) + ")";
QgsPGLayerItem *layerItem = new QgsPGLayerItem( this, name, mPath + "/" + name, layerType, layerProperty );
addChild( layerItem );
}
// ---------------------------------------------------------------------------
QgsPGRootItem::QgsPGRootItem( QgsDataItem* parent, QString name, QString path )
: QgsDataCollectionItem( parent, name, path )
@ -183,13 +234,12 @@ QgsPGRootItem::~QgsPGRootItem()
{
}
QVector<QgsDataItem*>QgsPGRootItem::createChildren()
QVector<QgsDataItem*> QgsPGRootItem::createChildren()
{
QVector<QgsDataItem*> connections;
foreach( QString connName, QgsPostgresConnection::connectionList() )
foreach( QString connName, QgsPostgresConn::connectionList() )
{
QgsDataItem * conn = new QgsPGConnectionItem( this, connName, mPath + "/" + connName );
connections.push_back( conn );
connections << new QgsPGConnectionItem( this, connName, mPath + "/" + connName );
}
return connections;
}
@ -205,12 +255,13 @@ QList<QAction*> QgsPGRootItem::actions()
return lst;
}
QWidget * QgsPGRootItem::paramWidget()
QWidget *QgsPGRootItem::paramWidget()
{
QgsPgSourceSelect *select = new QgsPgSourceSelect( 0, 0, true, true );
connect( select, SIGNAL( connectionsChanged() ), this, SLOT( connectionsChanged() ) );
return select;
}
void QgsPGRootItem::connectionsChanged()
{
refresh();
@ -224,22 +275,3 @@ void QgsPGRootItem::newConnection()
refresh();
}
}
// ---------------------------------------------------------------------------
QGISEXTERN QgsPgSourceSelect * selectWidget( QWidget * parent, Qt::WFlags fl )
{
// TODO: this should be somewhere else
return new QgsPgSourceSelect( parent, fl );
}
QGISEXTERN int dataCapabilities()
{
return QgsDataProvider::Database;
}
QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem )
{
Q_UNUSED( thePath );
return new QgsPGRootItem( parentItem, "PostGIS", "pg:" );
}

View File

@ -3,57 +3,13 @@
#include "qgsdataitem.h"
#include "qgspostgresprovider.h"
#include "qgspostgresconn.h"
#include "qgspgsourceselect.h"
class QgsPGConnectionItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsPGConnectionItem( QgsDataItem* parent, QString name, QString path );
~QgsPGConnectionItem();
QVector<QgsDataItem*> createChildren();
virtual bool equal( const QgsDataItem *other );
virtual QList<QAction*> actions();
QString mConnInfo;
QMap<QString, QVector<QgsPostgresLayerProperty> > mSchemasMap;
public slots:
void editConnection();
void deleteConnection();
};
// WMS Layers may be nested, so that they may be both QgsDataCollectionItem and QgsLayerItem
// We have to use QgsDataCollectionItem and support layer methods if necessary
class QgsPGLayerItem : public QgsLayerItem
{
Q_OBJECT
public:
QgsPGLayerItem( QgsDataItem* parent, QString name, QString path,
QString connInfo, QgsLayerItem::LayerType layerType, QgsPostgresLayerProperty layerProperties );
~QgsPGLayerItem();
QString createUri();
QString mConnInfo;
QgsPostgresLayerProperty mLayerProperty;
};
class QgsPGSchemaItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsPGSchemaItem( QgsDataItem* parent, QString name, QString path,
QString connInfo );
~QgsPGSchemaItem();
QVector<QgsDataItem*> createChildren();
protected:
QString mConnInfo;
};
class QgsPGRootItem;
class QgsPGConnectionItem;
class QgsPGSchemaItem;
class QgsPGLayerItem;
class QgsPGRootItem : public QgsDataCollectionItem
{
@ -73,5 +29,54 @@ class QgsPGRootItem : public QgsDataCollectionItem
void newConnection();
};
class QgsPGConnectionItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsPGConnectionItem( QgsDataItem* parent, QString name, QString path );
~QgsPGConnectionItem();
QVector<QgsDataItem*> createChildren();
virtual bool equal( const QgsDataItem *other );
virtual QList<QAction*> actions();
QgsPostgresConn *connection() const { return mConn; }
public slots:
void editConnection();
void deleteConnection();
void setLayerType( QgsPostgresLayerProperty layerProperty );
private:
QgsPostgresConn *mConn;
QMap<QString, QgsPGSchemaItem * > mSchemaMap;
};
class QgsPGSchemaItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsPGSchemaItem( QgsDataItem* parent, QString name, QString path );
~QgsPGSchemaItem();
QVector<QgsDataItem*> createChildren();
void addLayer( QgsPostgresLayerProperty layerProperty );
};
class QgsPGLayerItem : public QgsLayerItem
{
Q_OBJECT
public:
QgsPGLayerItem( QgsDataItem* parent, QString name, QString path, QgsLayerItem::LayerType layerType, QgsPostgresLayerProperty layerProperties );
~QgsPGLayerItem();
QString createUri();
private:
QgsPostgresLayerProperty mLayerProperty;
};
#endif // QGSPOSTGRESDATAITEMS_H

File diff suppressed because it is too large Load Diff

View File

@ -18,41 +18,20 @@
#ifndef QGSPOSTGRESPROVIDER_H
#define QGSPOSTGRESPROVIDER_H
extern "C"
{
#include <libpq-fe.h>
}
#include "qgsvectordataprovider.h"
#include "qgsrectangle.h"
#include "qgsvectorlayerimport.h"
#include <list>
#include <queue>
#include <fstream>
#include <set>
#include "qgspostgresconn.h"
#include <QVector>
#include <QQueue>
class QgsFeature;
class QgsField;
class QgsGeometry;
#include "qgsdatasourceuri.h"
/** Layer Property structure */
// TODO: Fill to Postgres/PostGIS specifications
struct QgsPostgresLayerProperty
{
// Postgres/PostGIS layer properties
QString type;
QString schemaName;
QString tableName;
QString geometryColName;
QStringList pkCols;
QString sql;
};
/**
\class QgsPostgresProvider
\brief Data provider for PostgreSQL/PostGIS layers.
@ -85,10 +64,10 @@ class QgsPostgresProvider : public QgsVectorDataProvider
* @param uri String containing the required parameters to connect to the database
* and query the table.
*/
QgsPostgresProvider( QString const & uri = "" );
QgsPostgresProvider( QString const &uri = "" );
//! Destructor
virtual ~ QgsPostgresProvider();
virtual ~QgsPostgresProvider();
/**
* Returns the permanent storage type for this layer as a friendly name.
@ -164,11 +143,6 @@ class QgsPostgresProvider : public QgsVectorDataProvider
*/
uint fieldCount() const;
/**
* Get the data source URI structure used by this layer
*/
QgsDataSourceURI& getURI();
/**
* Return a string representation of the endian-ness for the layer
*/
@ -184,9 +158,9 @@ class QgsPostgresProvider : public QgsVectorDataProvider
*/
virtual QgsRectangle extent();
/** * Get the name of the primary key for the layer
/** Determine the fields making up the primary key
*/
QString getPrimaryKey();
bool determinePrimaryKey();
/**
* Get the field information for the layer
@ -264,18 +238,12 @@ class QgsPostgresProvider : public QgsVectorDataProvider
/**
Changes geometries of existing features
@param geometry_map A std::map containing the feature IDs to change the geometries of.
@param geometry_map A QMap containing the feature IDs to change the geometries of.
the second map parameter being the new geometries themselves
@return true in case of success and false in case of failure
*/
bool changeGeometryValues( QgsGeometryMap & geometry_map );
//! Get the list of supported layers
bool supportedLayers( QVector<QgsPostgresLayerProperty> &layers,
bool searchGeometryColumnsOnly = true,
bool searchPublicOnly = true,
bool allowGeometrylessTables = false );
//! Get the postgres connection
PGconn * pgConnection();
@ -332,7 +300,6 @@ class QgsPostgresProvider : public QgsVectorDataProvider
*/
QString description() const;
signals:
/**
* This is emitted whenever the worker thread has fully calculated the
@ -354,44 +321,26 @@ class QgsPostgresProvider : public QgsVectorDataProvider
void repaintRequested();
private:
int providerId; // id to append to provider specific identified (like cursors)
int mProviderId; // id to append to provider specific identified (like cursors)
bool declareCursor( const QString &cursorName,
const QgsAttributeList &fetchAttributes,
bool fetchGeometry,
QString whereClause );
bool getFeature( PGresult *queryResult, int row, bool fetchGeometry,
bool getFeature( QgsPostgresResult &queryResult,
int row,
bool fetchGeometry,
QgsFeature &feature,
const QgsAttributeList &fetchAttributes );
QString pkParamWhereClause( int offset ) const;
QString whereClause( QgsFeatureId featureId ) const;
/** Gets information about the spatial tables */
bool getTableInfo( bool searchGeometryColumnsOnly, bool searchPublicOnly, bool allowGeometrylessTables );
/** get primary key candidates (all int4 columns) */
QStringList pkCandidates( QString schemaName, QString viewName );
bool hasSufficientPermsAndCapabilities();
qint64 getBinaryInt( PGresult *queryResult, int row, int col );
const QgsField &field( int index ) const;
/** Double quote a PostgreSQL identifier for placement in a SQL string.
*/
static QString quotedIdentifier( QString ident );
/** Quote a value for placement in a SQL string.
*/
static QString quotedValue( QString value );
/** expression to retrieve value
*/
QString fieldExpression( const QgsField &fld ) const;
/** Load the field list
*/
bool loadFields();
@ -414,30 +363,27 @@ class QgsPostgresProvider : public QgsVectorDataProvider
bool mFetching; // true if a cursor was declared
int mFetched; // number of retrieved features
std::vector < QgsFeature > features;
QgsFieldMap attributeFields;
QVector<QgsFeature> mFeatures;
QgsFieldMap mAttributeFields;
QString mDataComment;
//! Data source URI struct for this layer
QgsDataSourceURI mUri;
//! List of the supported layers
QVector<QgsPostgresLayerProperty> layersSupported;
/**
* Flag indicating if the layer data source is a valid PostgreSQL layer
*/
bool valid;
bool mValid;
/**
* provider references query (instead of a table)
*/
bool isQuery;
bool mIsQuery;
/**
* geometry is geography
*/
bool isGeography;
bool mIsGeography;
/**
* Name of the table with no schema
@ -458,327 +404,104 @@ class QgsPostgresProvider : public QgsVectorDataProvider
/**
* SQL statement used to limit the features retrieved
*/
QString sqlWhereClause;
QString mSqlWhereClause;
/**
* Primary key column for fetching features. If there is no primary key
* the oid is used to fetch features.
*/
QString primaryKey;
/**
* Primary key column is "real" primary key
*/
bool mIsDbPrimaryKey;
/**
* Data type for the primary key
*/
QString primaryKeyType;
/**
* Name of the geometry column in the table
*/
QString geometryColumn;
/**
* Geometry type
*/
QGis::WkbType geomType;
enum { pktUnknown, pktInt, pktTid, pktOid, pktFidMap } mPrimaryKeyType;
/**
* Spatial reference id of the layer
* List of primary key attributes for fetching features.
*/
QString srid;
/**
* Rectangle that contains the extent (bounding box) of the layer
*/
QgsRectangle layerExtent;
QList<int> mPrimaryKeyAttrs;
QString mPrimaryKeyDefault;
/**
* Number of features in the layer
*/
mutable long featuresCounted;
QString mGeometryColumn; //! name of the geometry column
QgsRectangle mLayerExtent; //! Rectangle that contains the extent (bounding box) of the layer
mutable long mFeaturesCounted; //! Number of features in the layer
QGis::WkbType mDetectedGeomType; //! geometry type detected in the database
QGis::WkbType mRequestedGeomType; //! geometry type requested in the uri
QString mDetectedSrid; //! Spatial reference detected in the database
QString mRequestedSrid; //! Spatial reference requested in the uri
/**
* Feature queue that GetNextFeature will retrieve from
* before the next fetch from PostgreSQL
*/
std::queue<QgsFeature> mFeatureQueue;
QQueue<QgsFeature> mFeatureQueue;
/**
* Maximal size of the feature queue
*/
int mFeatureQueueSize;
int mFeatureQueueSize; //! Maximal size of the feature queue
/**
* Flag indicating whether data from binary cursors must undergo an
* endian conversion prior to use
@note
XXX Umm, it'd be helpful to know what we're swapping from and to.
XXX Presumably this means swapping from big-endian (network) byte order
XXX to little-endian; but the inverse transaction is possible, too, and
XXX that's not reflected in this variable
*/
bool swapEndian;
bool deduceEndian();
bool getGeometryDetails();
/* Use estimated metadata. Uses fast table counts, geometry type and extent determination */
bool mUseEstimatedMetadata;
/* Disable support for SelectAtId */
bool mSelectAtIdDisabled;
bool mSelectAtIdDisabled; //! Disable support for SelectAtId
// Produces a QMessageBox with the given title and text. Doesn't
// return until the user has dismissed the dialog box.
static void showMessageBox( const QString& title, const QString &text );
static void showMessageBox( const QString& title, const QStringList &text );
// A simple class to store the rows of the sql executed in the
// findColumns() function.
class TT
{
public:
TT() {};
QString view_schema;
QString view_name;
QString view_column_name;
QString table_schema;
QString table_name;
QString column_name;
QString table_type;
QString column_type;
};
struct PGFieldNotFound
{
};
struct PGFieldNotFound {}; //! Exception to throw
struct PGException
{
PGException( PGresult *r ) : result( r )
{
}
PGException( QgsPostgresResult &r )
: mWhat( r.PQresultErrorMessage() )
{}
PGException( const PGException &e ) : result( e.result )
{
}
PGException( const PGException &e )
: mWhat( e.errorMessage() )
{}
~PGException()
{
if ( result )
PQclear( result );
}
{}
QString errorMessage() const
{
return result ?
QString::fromUtf8( PQresultErrorMessage( result ) ) :
tr( "unexpected PostgreSQL error" );
}
void showErrorMessage( QString title ) const
{
showMessageBox( title, errorMessage() );
return mWhat;
}
private:
PGresult *result;
QString mWhat;
};
// A simple class to store four strings
class SRC
{
public:
SRC() {};
SRC( QString s, QString r, QString c, QString t ) :
schema( s ), relation( r ), column( c ), type( t ) {};
QString schema, relation, column, type;
};
// A structure to store the underlying schema.table.column for
// each column in mSchemaName.mTableName
typedef std::map<QString, SRC> tableCols;
// A function that chooses a view column that is suitable for use
// a the qgis key column.
QString chooseViewColumn( const tableCols& cols );
// A function that determines if the given schema.table.column
// contains unqiue entries
bool uniqueData( QString query, QString colName );
// Function that populates the given cols structure.
void findColumns( tableCols& cols );
int mEnabledCapabilities;
/**Helper function that collects information about the origin and type of a view column.
Inputs are information about the column in the underlying table
(from information_schema.view_column_usage), the attribute name
in the view and the view definition. For view columns that refer
to other views, this function calls itself until a table entry is found.
@param ns namespace of underlying table
@param relname name of underlying relation
@param attname attribute name in underlying table
@param viewDefinition definition of this view
@param result
@return 0 in case of success*/
int SRCFromViewColumn( const QString& ns, const QString& relname, const QString& attname_table,
const QString& attname_view, const QString& viewDefinition, SRC& result ) const;
void appendGeomParam( QgsGeometry *geom, QStringList &param ) const;
void appendPkParams( QgsFeatureId fid, QStringList &param ) const;
int enabledCapabilities;
void appendGeomString( QgsGeometry *geom, QString &geomParam ) const;
QString paramValue( QString fieldvalue, const QString &defaultValue ) const;
class Conn
{
public:
Conn( PGconn *connection )
: ref( 1 )
, openCursors( 0 )
, conn( connection )
, gotPostgisVersion( false )
{
}
//! get postgis version string
QString postgisVersion();
//! get status of GEOS capability
bool hasGEOS();
//! get status of topology capability
bool hasTopology();
//! get status of GIST capability
bool hasGIST();
//! get status of PROJ4 capability
bool hasPROJ();
//! encode wkb in hex
bool useWkbHex() { return mUseWkbHex; }
//! major PostgreSQL version
int majorVersion() { return postgisVersionMajor; }
//! PostgreSQL version
int pgVersion() { return postgresqlVersion; }
//! run a query and free result buffer
bool PQexecNR( QString query, bool retry = true );
//! cursor handling
bool openCursor( QString cursorName, QString declare );
bool closeCursor( QString cursorName );
PGconn *pgConnection() { return conn; }
//
// libpq wrapper
//
// run a query and check for errors
PGresult *PQexec( QString query );
void PQfinish();
int PQsendQuery( QString query );
PGresult *PQgetResult();
PGresult *PQprepare( QString stmtName, QString query, int nParams, const Oid *paramTypes );
PGresult *PQexecPrepared( QString stmtName, const QStringList &params );
static Conn *connectDb( const QString &conninfo, bool readonly );
static void disconnectRW( Conn *&conn );
static void disconnectRO( Conn *&conn );
static void disconnect( QMap<QString, Conn *> &connections, Conn *&conn );
private:
int ref;
int openCursors;
PGconn *conn;
//! GEOS capability
bool geosAvailable;
//! Topology capability
bool topologyAvailable;
//! PostGIS version string
QString postgisVersionInfo;
//! Are postgisVersionMajor, postgisVersionMinor, geosAvailable, gistAvailable, projAvailable, topologyAvailable valid?
bool gotPostgisVersion;
//! PostgreSQL version
int postgresqlVersion;
//! PostGIS major version
int postgisVersionMajor;
//! PostGIS minor version
int postgisVersionMinor;
//! GIST capability
bool gistAvailable;
//! PROJ4 capability
bool projAvailable;
//! encode wkb in hex
bool mUseWkbHex;
static QMap<QString, Conn *> connectionsRW;
static QMap<QString, Conn *> connectionsRO;
static QMap<QString, QString> passwordCache;
};
class Result
{
public:
Result( PGresult *theRes = 0 ) : res( theRes ) {}
~Result() { if ( res ) PQclear( res ); }
operator PGresult *() { return res; }
Result &operator=( PGresult *theRes ) { if ( res ) PQclear( res ); res = theRes; return *this; }
private:
PGresult *res;
};
/**
* Connection pointers
*/
Conn *connectionRO;
Conn *connectionRW;
QgsPostgresConn *mConnectionRO; //! read-only database connection (initially)
QgsPostgresConn *mConnectionRW; //! read-write database connection (on update)
//! establish read-write connection
bool connectRW()
{
if ( connectionRW )
return connectionRW;
if ( mConnectionRW )
return mConnectionRW;
connectionRW = Conn::connectDb( mUri.connectionInfo(), false );
mConnectionRW = QgsPostgresConn::connectDb( mUri.connectionInfo(), false );
return connectionRW;
return mConnectionRW;
}
void disconnectDb();
static int providerIds;
static QString quotedIdentifier( QString ident ) { return QgsPostgresConn::quotedIdentifier( ident ); }
static QString quotedValue( QVariant value ) { return QgsPostgresConn::quotedValue( value ); }
QString primaryKeyDefault();
void parseView();
/**
* Default value for primary key
*/
QString mPrimaryKeyDefault;
#if 0
/** used to cache the lastest fetched features */
QHash<QgsFeatureId, QgsFeature> mFeatureMap;
QList<QgsFeatureId> mPriorityIds;
#endif
static int sProviderIds;
static const int sFeatureQueueSize;
QMap<QVariant, QgsFeatureId> mKeyToFid; // map key values to feature id
QMap<QgsFeatureId, QVariant> mFidToKey; // map feature back to fea
QgsFeatureId mFidCounter; // next feature id if map is used
QgsFeatureId lookupFid( const QVariant &v ); // lookup existing mapping or add a new one
};
#endif