src/app/qgsdbsourceselect.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                              qgsdbsourceselect.cpp
00003        Dialog to select PostgreSQL layer(s) and add it to the map canvas
00004                               -------------------
00005 begin                : Sat Jun 22 2002
00006 copyright            : (C) 2002 by Gary E.Sherman
00007 email                : sherman at mrcc.com
00008  ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 /* $Id: qgsdbsourceselect.cpp 9502 2008-10-20 20:24:36Z timlinux $ */
00019 
00020 #include "qgsdbsourceselect.h"
00021 
00022 #include "qgisapp.h"
00023 #include "qgslogger.h"
00024 #include "qgsapplication.h"
00025 #include "qgscontexthelp.h"
00026 #include "qgsnewconnection.h"
00027 #include "qgspgquerybuilder.h"
00028 #include "qgsdatasourceuri.h"
00029 
00030 #include <QInputDialog>
00031 #include <QMessageBox>
00032 #include <QSettings>
00033 #include <QTextStream>
00034 #include <QTableWidgetItem>
00035 #include <QHeaderView>
00036 #include <QStringList>
00037 
00038 #include <cassert>
00039 #include <iostream>
00040 
00041 QgsDbSourceSelect::QgsDbSourceSelect( QWidget *parent, Qt::WFlags fl )
00042     : QDialog( parent, fl ), mColumnTypeThread( NULL ), pd( 0 )
00043 {
00044   setupUi( this );
00045   btnAdd->setEnabled( false );
00046   populateConnectionList();
00047 
00048   mSearchModeComboBox->addItem( tr( "Wildcard" ) );
00049   mSearchModeComboBox->addItem( tr( "RegExp" ) );
00050 
00051   mSearchColumnComboBox->addItem( tr( "All" ) );
00052   mSearchColumnComboBox->addItem( tr( "Schema" ) );
00053   mSearchColumnComboBox->addItem( tr( "Table" ) );
00054   mSearchColumnComboBox->addItem( tr( "Type" ) );
00055   mSearchColumnComboBox->addItem( tr( "Geometry column" ) );
00056   mSearchColumnComboBox->addItem( tr( "Sql" ) );
00057 
00058   mProxyModel.setParent( this );
00059   mProxyModel.setFilterKeyColumn( -1 );
00060   mProxyModel.setFilterCaseSensitivity( Qt::CaseInsensitive );
00061   mProxyModel.setDynamicSortFilter( true );
00062   mProxyModel.setSourceModel( &mTableModel );
00063   mTablesTreeView->setModel( &mProxyModel );
00064   mTablesTreeView->setSortingEnabled( true );
00065 
00066   mSearchGroupBox->hide();
00067   connect( mTablesTreeView, SIGNAL( doubleClicked( const QModelIndex& ) ), this, SLOT( setSql( const QModelIndex& ) ) );
00068 
00069   //for Qt < 4.3.2, passing -1 to include all model columns
00070   //in search does not seem to work
00071   mSearchColumnComboBox->setCurrentIndex( 2 );
00072 }
00074 // Slot for adding a new connection
00075 void QgsDbSourceSelect::on_btnNew_clicked()
00076 {
00077   addNewConnection();
00078 }
00079 // Slot for deleting an existing connection
00080 void QgsDbSourceSelect::on_btnDelete_clicked()
00081 {
00082   deleteConnection();
00083 }
00084 // Slot for performing action when the Add button is clicked
00085 void QgsDbSourceSelect::on_btnAdd_clicked()
00086 {
00087   addTables();
00088 }
00089 
00090 // Slot for editing a connection
00091 void QgsDbSourceSelect::on_btnEdit_clicked()
00092 {
00093   editConnection();
00094 }
00095 
00096 // Slot for showing help
00097 void QgsDbSourceSelect::on_btnHelp_clicked()
00098 {
00099   showHelp();
00100 }
00103 // Remember which database is selected
00104 void QgsDbSourceSelect::on_cmbConnections_activated( int )
00105 {
00106   dbChanged();
00107 }
00108 
00109 void QgsDbSourceSelect::on_mSearchOptionsButton_clicked()
00110 {
00111   if ( mSearchGroupBox->isVisible() )
00112   {
00113     mSearchGroupBox->hide();
00114   }
00115   else
00116   {
00117     mSearchGroupBox->show();
00118   }
00119 }
00120 
00121 void QgsDbSourceSelect::on_mSearchTableEdit_textChanged( const QString & text )
00122 {
00123   if ( mSearchModeComboBox->currentText() == tr( "Wildcard" ) )
00124   {
00125     mProxyModel._setFilterWildcard( text );
00126   }
00127   else if ( mSearchModeComboBox->currentText() == tr( "RegExp" ) )
00128   {
00129     mProxyModel._setFilterRegExp( text );
00130   }
00131 }
00132 
00133 void QgsDbSourceSelect::on_mSearchColumnComboBox_currentIndexChanged( const QString & text )
00134 {
00135   if ( text == tr( "All" ) )
00136   {
00137     mProxyModel.setFilterKeyColumn( -1 );
00138   }
00139   else if ( text == tr( "Schema" ) )
00140   {
00141     mProxyModel.setFilterKeyColumn( 0 );
00142   }
00143   else if ( text == tr( "Table" ) )
00144   {
00145     mProxyModel.setFilterKeyColumn( 1 );
00146   }
00147   else if ( text == tr( "Type" ) )
00148   {
00149     mProxyModel.setFilterKeyColumn( 2 );
00150   }
00151   else if ( text == tr( "Geometry column" ) )
00152   {
00153     mProxyModel.setFilterKeyColumn( 3 );
00154   }
00155   else if ( text == tr( "Sql" ) )
00156   {
00157     mProxyModel.setFilterKeyColumn( 4 );
00158   }
00159 }
00160 
00161 void QgsDbSourceSelect::on_mSearchModeComboBox_currentIndexChanged( const QString & text )
00162 {
00163   on_mSearchTableEdit_textChanged( mSearchTableEdit->text() );
00164 }
00165 
00166 void QgsDbSourceSelect::setLayerType( QString schema,
00167                                       QString table, QString column,
00168                                       QString type )
00169 {
00170   mTableModel.setGeometryTypesForTable( schema, table, column, type );
00171   mTablesTreeView->sortByColumn( 1, Qt::AscendingOrder );
00172   mTablesTreeView->sortByColumn( 0, Qt::AscendingOrder );
00173 }
00174 
00175 QString QgsDbSourceSelect::makeGeomQuery( QString schema,
00176     QString table, QString column )
00177 {
00178   return QString( "select distinct "
00179                   "case"
00180                   " when geometrytype(%1) IN ('POINT','MULTIPOINT') THEN 'POINT'"
00181                   " when geometrytype(%1) IN ('LINESTRING','MULTILINESTRING') THEN 'LINESTRING'"
00182                   " when geometrytype(%1) IN ('POLYGON','MULTIPOLYGON') THEN 'POLYGON'"
00183                   " end "
00184                   "from \"%2\".\"%3\"" ).arg( "\"" + column + "\"" ).arg( schema ).arg( table );
00185 }
00186 
00187 QgsDbSourceSelect::~QgsDbSourceSelect()
00188 {
00189   PQfinish( pd );
00190 }
00191 void QgsDbSourceSelect::populateConnectionList()
00192 {
00193   QSettings settings;
00194   settings.beginGroup( "/PostgreSQL/connections" );
00195   QStringList keys = settings.childGroups();
00196   QStringList::Iterator it = keys.begin();
00197   cmbConnections->clear();
00198   while ( it != keys.end() )
00199   {
00200     cmbConnections->addItem( *it );
00201     ++it;
00202   }
00203   settings.endGroup();
00204   setConnectionListPosition();
00205 }
00206 void QgsDbSourceSelect::addNewConnection()
00207 {
00208   QgsNewConnection *nc = new QgsNewConnection( this );
00209 
00210   if ( nc->exec() )
00211   {
00212     populateConnectionList();
00213   }
00214 }
00215 void QgsDbSourceSelect::editConnection()
00216 {
00217   QgsNewConnection *nc = new QgsNewConnection( this, cmbConnections->currentText() );
00218 
00219   if ( nc->exec() )
00220   {
00221     nc->saveConnection();
00222   }
00223 }
00224 
00225 void QgsDbSourceSelect::deleteConnection()
00226 {
00227   QSettings settings;
00228   QString key = "/Postgresql/connections/" + cmbConnections->currentText();
00229   QString msg =
00230     tr( "Are you sure you want to remove the " ) + cmbConnections->currentText() + tr( " connection and all associated settings?" );
00231   QMessageBox::StandardButton result = QMessageBox::information( this, tr( "Confirm Delete" ), msg, QMessageBox::Ok | QMessageBox::Cancel );
00232   if ( result == QMessageBox::Ok )
00233   {
00234     settings.remove( key + "/host" );
00235     settings.remove( key + "/database" );
00236     settings.remove( key + "/username" );
00237     settings.remove( key + "/password" );
00238     settings.remove( key + "/port" );
00239     settings.remove( key + "/save" );
00240     settings.remove( key );
00241     //if(!success){
00242     //  QMessageBox::information(this,"Unable to Remove","Unable to remove the connection " + cmbConnections->currentText());
00243     //}
00244     cmbConnections->removeItem( cmbConnections->currentIndex() );  // populateConnectionList();
00245     setConnectionListPosition();
00246   }
00247 }
00248 void QgsDbSourceSelect::addTables()
00249 {
00250   m_selectedTables.clear();
00251 
00252   typedef QMap<int, QVector<QString> > schemaInfo;
00253   QMap<QString, schemaInfo> dbInfo;
00254 
00255   QItemSelection selection = mTablesTreeView->selectionModel()->selection();
00256   QModelIndexList selectedIndices = selection.indexes();
00257   QStandardItem* currentItem = 0;
00258 
00259   QModelIndexList::const_iterator selected_it = selectedIndices.constBegin();
00260   for ( ; selected_it != selectedIndices.constEnd(); ++selected_it )
00261   {
00262     if ( !selected_it->parent().isValid() )
00263     {
00264       //top level items only contain the schema names
00265       continue;
00266     }
00267     currentItem = mTableModel.itemFromIndex( mProxyModel.mapToSource( *selected_it ) );
00268     if ( !currentItem )
00269     {
00270       continue;
00271     }
00272 
00273     QString currentSchemaName = currentItem->parent()->text();
00274 
00275     int currentRow = currentItem->row();
00276     int currentColumn = currentItem->column();
00277 
00278     if ( dbInfo[currentSchemaName][currentRow].size() == 0 )
00279     {
00280       dbInfo[currentSchemaName][currentRow].resize( 5 );
00281     }
00282 
00283     dbInfo[currentSchemaName][currentRow][currentColumn] = currentItem->text();
00284   }
00285 
00286   //now traverse all the schemas and table infos
00287   QString schemaName, tableName, geomColumnName, sql;
00288   QString query;
00289 
00290   QMap<QString, schemaInfo>::const_iterator schema_it = dbInfo.constBegin();
00291   for ( ; schema_it != dbInfo.constEnd(); ++schema_it )
00292   {
00293     schemaInfo scheme = schema_it.value();
00294     schemaInfo::const_iterator entry_it = scheme.constBegin();
00295     for ( ; entry_it != scheme.constEnd(); ++entry_it )
00296     {
00297       schemaName = entry_it->at( 0 );
00298       tableName = entry_it->at( 1 );
00299       geomColumnName = entry_it->at( 3 );
00300       sql = entry_it->at( 4 );
00301 
00302       if ( geomColumnName.contains( " AS " ) )
00303       {
00304         int a = geomColumnName.indexOf( " AS " );
00305         QString typeName = geomColumnName.mid( a + 4 ); //only the type name
00306         geomColumnName = geomColumnName.mid( 0, a ); //only the geom column name
00307 
00308         if ( !sql.isEmpty() )
00309         {
00310           sql += " AND ";
00311         }
00312         if ( typeName == "POINT" )
00313         {
00314           sql += QString( "GeometryType(\"%1\") IN ('POINT','MULTIPOINT')" ).arg( geomColumnName );
00315         }
00316         else if ( typeName == "LINESTRING" )
00317         {
00318           sql += QString( "GeometryType(\"%1\") IN ('LINESTRING','MULTILINESTRING')" ).arg( geomColumnName );
00319         }
00320         else if ( typeName == "POLYGON" )
00321         {
00322           sql += QString( "GeometryType(\"%1\") IN ('POLYGON','MULTIPOLYGON')" ).arg( geomColumnName );
00323         }
00324         else
00325         {
00326           continue;
00327         }
00328       }
00329 
00330       query = "\"" + schemaName + "\".\"" + tableName + "\" " + "(" + geomColumnName + ") sql=" + sql;
00331 
00332       m_selectedTables.push_back( query );
00333     }
00334   }
00335 
00336   if ( m_selectedTables.empty() )
00337   {
00338     QMessageBox::information( this, tr( "Select Table" ), tr( "You must select a table in order to add a Layer." ) );
00339   }
00340   else
00341   {
00342     accept();
00343   }
00344 }
00345 
00346 void QgsDbSourceSelect::on_btnConnect_clicked()
00347 {
00348   if ( mColumnTypeThread )
00349   {
00350     mColumnTypeThread->stop();
00351     mColumnTypeThread = 0;
00352   }
00353 
00354   QModelIndex rootItemIndex = mTableModel.indexFromItem( mTableModel.invisibleRootItem() );
00355   mTableModel.removeRows( 0, mTableModel.rowCount( rootItemIndex ), rootItemIndex );
00356 
00357   // populate the table list
00358   QSettings settings;
00359 
00360   bool makeConnection = true;
00361   QString key = "/PostgreSQL/connections/" + cmbConnections->currentText();
00362 
00363   QString database = settings.value( key + "/database" ).toString();
00364   QString username = settings.value( key + "/username" ).toString();
00365   QString password = settings.value( key + "/password" ).toString();
00366 
00367   if ( password.isEmpty() )
00368   {
00369     // get password from user
00370     makeConnection = false;
00371     password = QInputDialog::getText( this, tr( "Password for " ) + username,
00372                                       tr( "Please enter your password:" ),
00373                                       QLineEdit::Password, QString::null, &makeConnection );
00374     // allow null password entry in case its valid for the database
00375   }
00376 
00377   QgsDataSourceURI uri;
00378   uri.setConnection( settings.value( key + "/host" ).toString(),
00379                      settings.value( key + "/port" ).toString(),
00380                      database,
00381                      settings.value( key + "/username" ).toString(),
00382                      password );
00383 
00384   bool searchPublicOnly = settings.value( key + "/publicOnly" ).toBool();
00385   bool searchGeometryColumnsOnly = settings.value( key + "/geometryColumnsOnly" ).toBool();
00386 
00387   // Need to escape the password to allow for single quotes and backslashes
00388 
00389   QgsDebugMsg( "Connection info: " + uri.connectionInfo() );
00390 
00391   if ( makeConnection )
00392   {
00393     m_connectionInfo = uri.connectionInfo();
00394     //qDebug(m_connectionInfo);
00395     // Tidy up an existing connection if one exists.
00396     if ( pd != 0 )
00397       PQfinish( pd );
00398 
00399     pd = PQconnectdb( m_connectionInfo.toLocal8Bit() );  // use what is set based on locale; after connecting, use Utf8
00400     if ( PQstatus( pd ) == CONNECTION_OK )
00401     {
00402       //qDebug("Connection succeeded");
00403       // tell the DB that we want text encoded in UTF8
00404       PQsetClientEncoding( pd, QString( "UNICODE" ).toLocal8Bit() );
00405 
00406       // get the list of suitable tables and columns and populate the UI
00407       geomCol details;
00408 
00409       if ( getTableInfo( pd, searchGeometryColumnsOnly, searchPublicOnly ) )
00410       {
00411         // Start the thread that gets the geometry type for relations that
00412         // may take a long time to return
00413         if ( mColumnTypeThread != NULL )
00414         {
00415           connect( mColumnTypeThread, SIGNAL( setLayerType( QString, QString, QString, QString ) ),
00416                    this, SLOT( setLayerType( QString, QString, QString, QString ) ) );
00417 
00418           // Do it in a thread.
00419           mColumnTypeThread->start();
00420         }
00421       }
00422       else
00423       {
00424         qDebug( "Unable to get list of spatially enabled tables from the database" );
00425         qDebug( PQerrorMessage( pd ) );
00426       }
00427       // BEGIN CHANGES ECOS
00428       if ( cmbConnections->count() > 0 )
00429         btnAdd->setEnabled( true );
00430       // END CHANGES ECOS
00431     }
00432     else
00433     {
00434       QMessageBox::warning( this, tr( "Connection failed" ),
00435                             tr
00436                             ( "Connection to %1 on %2 failed. Either the database is down or your settings are incorrect.%3Check your username and password and try again.%4The database said:%5%6" ).
00437                             arg( settings.value( key + "/database" ).toString() ).arg( settings.value( key + "/host" ).toString() ).arg( "\n\n" ).arg( "\n\n" ).arg( "\n" ).arg( QString::fromUtf8( PQerrorMessage( pd ) ) ) );
00438     }
00439   }
00440 
00441   mTablesTreeView->sortByColumn( 1, Qt::AscendingOrder );
00442   mTablesTreeView->sortByColumn( 0, Qt::AscendingOrder );
00443 
00444   //if we have only one schema item, expand it by default
00445   int numTopLevelItems = mTableModel.invisibleRootItem()->rowCount();
00446   if ( numTopLevelItems < 2 || mTableModel.tableCount() < 20 )
00447   {
00448     //expand all the toplevel items
00449     for ( int i = 0; i < numTopLevelItems; ++i )
00450     {
00451       mTablesTreeView->expand( mProxyModel.mapFromSource( mTableModel.indexFromItem( mTableModel.invisibleRootItem()->child( i ) ) ) );
00452     }
00453   }
00454 }
00455 
00456 QStringList QgsDbSourceSelect::selectedTables()
00457 {
00458   return m_selectedTables;
00459 }
00460 
00461 QString QgsDbSourceSelect::connectionInfo()
00462 {
00463   return m_connectionInfo;
00464 }
00465 
00466 void QgsDbSourceSelect::setSql( const QModelIndex& index )
00467 {
00468   if ( !index.parent().isValid() )
00469   {
00470     qWarning( "schema item found" );
00471     return;
00472   }
00473 
00474   if ( pd == 0 )
00475   {
00476     return;
00477   }
00478 
00479   //create "Schema"."Table" and find out existing sql string
00480   QModelIndex schemaSibling = index.sibling( index.row(), 0 );
00481   QModelIndex tableSibling = index.sibling( index.row(), 1 );
00482   if ( !schemaSibling.isValid() || !tableSibling.isValid() )
00483   {
00484     return;
00485   }
00486 
00487   QString schemaName = mTableModel.itemFromIndex( mProxyModel.mapToSource( schemaSibling ) )->text();
00488   QString tableName = mTableModel.itemFromIndex( mProxyModel.mapToSource( tableSibling ) )->text();
00489   QString tableString = "\"" + schemaName + "\".\"" + tableName + "\"";
00490   qWarning( tableString.toUtf8() );
00491 
00492   QString currentSql;
00493   QModelIndex sqlSibling = index.sibling( index.row(), 4 );
00494   if ( sqlSibling.isValid() )
00495   {
00496     currentSql = mTableModel.itemFromIndex( mProxyModel.mapToSource( sqlSibling ) )->text();
00497   }
00498 
00499   // create a query builder object
00500   QgsPgQueryBuilder * pgb = new QgsPgQueryBuilder( tableString, pd, this );
00501   // set the current sql in the query builder sql box
00502   pgb->setSql( currentSql );
00503   // set the PG connection object so it can be used to fetch the
00504   // fields for the table, get sample values, and test the query
00505   pgb->setConnection( pd );
00506   // show the dialog
00507   if ( pgb->exec() )
00508   {
00509     mTableModel.setSql( mProxyModel.mapToSource( index ), pgb->sql() );
00510   }
00511 }
00512 
00513 void QgsDbSourceSelect::addSearchGeometryColumn( const QString &schema, const QString &table, const QString &column )
00514 {
00515   // store the column details and do the query in a thread
00516   if ( mColumnTypeThread == NULL )
00517   {
00518     mColumnTypeThread = new QgsGeomColumnTypeThread();
00519     mColumnTypeThread->setConnInfo( m_connectionInfo );
00520   }
00521   mColumnTypeThread->addGeometryColumn( schema, table, column );
00522 }
00523 
00524 bool QgsDbSourceSelect::getTableInfo( PGconn *pg, bool searchGeometryColumnsOnly, bool searchPublicOnly )
00525 {
00526   bool ok = false;
00527   QApplication::setOverrideCursor( Qt::WaitCursor );
00528 
00529   // The following query returns only tables that exist and the user has SELECT privilege on.
00530   // Can't use regclass here because table must exist, else error occurs.
00531   QString sql = "select * from geometry_columns,pg_class,pg_namespace "
00532                 "where relname=f_table_name and f_table_schema=nspname "
00533                 "and pg_namespace.oid = pg_class.relnamespace "
00534                 "and has_schema_privilege(pg_namespace.nspname,'usage') "
00535                 "and has_table_privilege('\"'||pg_namespace.nspname||'\".\"'||pg_class.relname||'\"','select') " // user has select privilege
00536                 "order by f_table_schema,f_table_name";
00537 
00538   PGresult *result = PQexec( pg, sql.toUtf8() );
00539   if ( result )
00540   {
00541     if ( PQresultStatus( result ) != PGRES_TUPLES_OK )
00542     {
00543       QMessageBox::warning( this,
00544                             tr( "Accessible tables could not be determined" ),
00545                             QString( tr( "Database connection was successful, but the accessible tables could not be determined.\n\n"
00546                                          "The error message from the database was:\n%1\n" ) )
00547                             .arg( QString::fromUtf8( PQresultErrorMessage( result ) ) ) );
00548     }
00549     else if ( PQntuples( result ) == 0 )
00550     {
00551       QMessageBox::warning( this, tr( "No accessible tables found" ),
00552                             tr
00553                             ( "Database connection was successful, but no accessible tables were found.\n\n"
00554                               "Please verify that you have SELECT privilege on a table carrying PostGIS\n"
00555                               "geometry." ) );
00556     }
00557     else
00558     {
00559       for ( int idx = 0; idx < PQntuples( result ); idx++ )
00560       {
00561         QString tableName = QString::fromUtf8( PQgetvalue( result, idx, PQfnumber( result, QString( "f_table_name" ).toUtf8() ) ) );
00562         QString schemaName = QString::fromUtf8( PQgetvalue( result, idx, PQfnumber( result, QString( "f_table_schema" ).toUtf8() ) ) );
00563 
00564         QString column = QString::fromUtf8( PQgetvalue( result, idx, PQfnumber( result, QString( "f_geometry_column" ).toUtf8() ) ) );
00565         QString type = QString::fromUtf8( PQgetvalue( result, idx, PQfnumber( result, QString( "type" ).toUtf8() ) ) );
00566 
00567         QString as = "";
00568         if ( type == "GEOMETRY" && !searchGeometryColumnsOnly )
00569         {
00570           addSearchGeometryColumn( schemaName, tableName,  column );
00571           as = type = "WAITING";
00572         }
00573 
00574         mTableModel.addTableEntry( type, schemaName, tableName, column, "" );
00575       }
00576     }
00577     ok = true;
00578   }
00579   PQclear( result );
00580 
00581   //search for geometry columns in tables that are not in the geometry_columns metatable
00582   QApplication::restoreOverrideCursor();
00583   if ( searchGeometryColumnsOnly )
00584   {
00585     return ok;
00586   }
00587 
00588   // Now have a look for geometry columns that aren't in the
00589   // geometry_columns table. This code is specific to postgresql,
00590   // but an equivalent query should be possible in other
00591   // databases.
00592   sql = "select pg_class.relname,pg_namespace.nspname,pg_attribute.attname,pg_class.relkind "
00593         "from pg_attribute, pg_class, pg_namespace "
00594         "where pg_namespace.oid = pg_class.relnamespace "
00595         "and pg_attribute.attrelid = pg_class.oid "
00596         "and ("
00597         "pg_attribute.atttypid = regtype('geometry')"
00598         " or "
00599         "pg_attribute.atttypid IN (select oid FROM pg_type WHERE typbasetype=regtype('geometry'))"
00600         ") "
00601         "and has_schema_privilege(pg_namespace.nspname,'usage') "
00602         "and has_table_privilege('\"'||pg_namespace.nspname||'\".\"'||pg_class.relname||'\"','select') ";
00603   // user has select privilege
00604   if ( searchPublicOnly )
00605     sql += "and pg_namespace.nspname = 'public' ";
00606 
00607   sql += "and not exists (select * from geometry_columns WHERE pg_namespace.nspname=f_table_schema AND pg_class.relname=f_table_name) "
00608          "and pg_class.relkind in ('v', 'r')"; // only from views and relations (tables)
00609 
00610   result = PQexec( pg, sql.toUtf8() );
00611 
00612   for ( int i = 0; i < PQntuples( result ); i++ )
00613   {
00614     // Have the column name, schema name and the table name. The concept of a
00615     // catalog doesn't exist in postgresql so we ignore that, but we
00616     // do need to get the geometry type.
00617 
00618     // Make the assumption that the geometry type for the first
00619     // row is the same as for all other rows.
00620 
00621     QString table  = QString::fromUtf8( PQgetvalue( result, i, 0 ) ); // relname
00622     QString schema = QString::fromUtf8( PQgetvalue( result, i, 1 ) ); // nspname
00623     QString column = QString::fromUtf8( PQgetvalue( result, i, 2 ) ); // attname
00624     QString relkind = QString::fromUtf8( PQgetvalue( result, i, 3 ) ); // relation kind
00625 
00626     addSearchGeometryColumn( schema, table, column );
00627     //details.push_back(geomPair(fullDescription(schema, table, column, "WAITING"), "WAITING"));
00628     mTableModel.addTableEntry( "Waiting", schema, table, column, "" );
00629   }
00630   ok = true;
00631 
00632   PQclear( result );
00633   return ok;
00634 }
00635 
00636 #if 0 // this function is never called - smizuno
00637 bool QgsDbSourceSelect::getGeometryColumnInfo( PGconn *pg,
00638     geomCol& details, bool searchGeometryColumnsOnly,
00639     bool searchPublicOnly )
00640 {
00641   bool ok = false;
00642 
00643   QApplication::setOverrideCursor( Qt::waitCursor );
00644 
00645   QString sql = "select * from geometry_columns";
00646   // where f_table_schema ='" + settings.value(key + "/database").toString() + "'";
00647   sql += " order by f_table_schema,f_table_name";
00648   //qDebug("Fetching tables using: " + sql);
00649   PGresult *result = PQexec( pg, sql.toUtf8() );
00650   if ( result )
00651   {
00652     QString msg;
00653     QTextStream( &msg ) << "Fetched " << PQntuples( result ) << " tables from database";
00654     //qDebug(msg);
00655     for ( int idx = 0; idx < PQntuples( result ); idx++ )
00656     {
00657       // Be a bit paranoid and check that the table actually
00658       // exists. This is not done as a subquery in the query above
00659       // because I can't get it to work correctly when there are tables
00660       // with capital letters in the name.
00661 
00662       // Take care to deal with tables with the same name but in different schema.
00663       QString tableName = QString::fromUtf8( PQgetvalue( result, idx, PQfnumber( result, "f_table_name" ) ) );
00664       QString schemaName = QString::fromUtf8( PQgetvalue( result, idx, PQfnumber( result, "f_table_schema" ) ) );
00665       sql = "select oid from pg_class where relname = '" + tableName + "'";
00666       if ( schemaName.length() > 0 )
00667         sql += " and relnamespace = (select oid from pg_namespace where nspname = '" +
00668                schemaName + "')";
00669 
00670       PGresult* exists = PQexec( pg, sql.toUtf8() );
00671       if ( PQntuples( exists ) == 1 )
00672       {
00673         QString column = QString::fromUtf8( PQgetvalue( result, idx, PQfnumber( result, "f_geometry_column" ) ) );
00674         QString type = QString::fromUtf8( PQgetvalue( result, idx, PQfnumber( result, "type" ) ) );
00675 
00676         QString as = "";
00677         if ( type == "GEOMETRY" && !searchGeometryColumnsOnly )
00678         {
00679           addSearchGeometryColumn( schemaName, tableName,  column );
00680           as = type = "WAITING";
00681         }
00682 
00683         details.push_back( geomPair( fullDescription( schemaName, tableName, column, as ), type ) );
00684       }
00685       PQclear( exists );
00686     }
00687     ok = true;
00688   }
00689   PQclear( result );
00690 
00691   QApplication::restoreOverrideCursor();
00692 
00693   if ( searchGeometryColumnsOnly )
00694     return ok;
00695 
00696   // Now have a look for geometry columns that aren't in the
00697   // geometry_columns table. This code is specific to postgresql,
00698   // but an equivalent query should be possible in other
00699   // databases.
00700   sql = "select pg_class.relname, pg_namespace.nspname, pg_attribute.attname, "
00701         "pg_class.relkind from "
00702         "pg_attribute, pg_class, pg_type, pg_namespace where pg_type.typname = 'geometry' and "
00703         "pg_attribute.atttypid = pg_type.oid and pg_attribute.attrelid = pg_class.oid ";
00704 
00705   if ( searchPublicOnly )
00706     sql += "and pg_namespace.nspname = 'public' ";
00707 
00708   sql += "and cast(pg_class.relname as character varying) not in "
00709          "(select f_table_name from geometry_columns) "
00710          "and pg_namespace.oid = pg_class.relnamespace "
00711          "and pg_class.relkind in ('v', 'r')"; // only from views and relations (tables)
00712 
00713   result = PQexec( pg, sql.toUtf8() );
00714 
00715   for ( int i = 0; i < PQntuples( result ); i++ )
00716   {
00717     // Have the column name, schema name and the table name. The concept of a
00718     // catalog doesn't exist in postgresql so we ignore that, but we
00719     // do need to get the geometry type.
00720 
00721     // Make the assumption that the geometry type for the first
00722     // row is the same as for all other rows.
00723 
00724     QString table  = QString::fromUtf8( PQgetvalue( result, i, 0 ) ); // relname
00725     QString schema = QString::fromUtf8( PQgetvalue( result, i, 1 ) ); // nspname
00726     QString column = QString::fromUtf8( PQgetvalue( result, i, 2 ) ); // attname
00727     QString relkind = QString::fromUtf8( PQgetvalue( result, i, 3 ) ); // relation kind
00728 
00729     addSearchGeometryColumn( schema, table, column );
00730     details.push_back( geomPair( fullDescription( schema, table, column, "WAITING" ), "WAITING" ) );
00731   }
00732   ok = true;
00733 
00734   PQclear( result );
00735 
00736   return ok;
00737 }
00738 #endif
00739 
00740 void QgsDbSourceSelect::showHelp()
00741 {
00742   QgsContextHelp::run( context_id );
00743 }
00744 
00745 QString QgsDbSourceSelect::fullDescription( QString schema, QString table,
00746     QString column, QString type )
00747 {
00748   QString full_desc = "";
00749   if ( schema.length() > 0 )
00750     full_desc = '"' + schema + "\".\"";
00751   full_desc += table + "\" (" + column + ") " + type;
00752   return full_desc;
00753 }
00754 
00755 void QgsDbSourceSelect::dbChanged()
00756 {
00757   // Remember which database was selected.
00758   QSettings settings;
00759   settings.setValue( "/PostgreSQL/connections/selected",
00760                      cmbConnections->currentText() );
00761 }
00762 
00763 void QgsDbSourceSelect::setConnectionListPosition()
00764 {
00765   QSettings settings;
00766   // If possible, set the item currently displayed database
00767   QString toSelect = settings.value( "/PostgreSQL/connections/selected" ).toString();
00768   // Does toSelect exist in cmbConnections?
00769   bool set = false;
00770   for ( int i = 0; i < cmbConnections->count(); ++i )
00771     if ( cmbConnections->itemText( i ) == toSelect )
00772     {
00773       cmbConnections->setCurrentIndex( i );
00774       set = true;
00775       break;
00776     }
00777   // If we couldn't find the stored item, but there are some,
00778   // default to the last item (this makes some sense when deleting
00779   // items as it allows the user to repeatidly click on delete to
00780   // remove a whole lot of items).
00781   if ( !set && cmbConnections->count() > 0 )
00782   {
00783     // If toSelect is null, then the selected connection wasn't found
00784     // by QSettings, which probably means that this is the first time
00785     // the user has used qgis with database connections, so default to
00786     // the first in the list of connetions. Otherwise default to the last.
00787     if ( toSelect.isNull() )
00788       cmbConnections->setCurrentIndex( 0 );
00789     else
00790       cmbConnections->setCurrentIndex( cmbConnections->count() - 1 );
00791   }
00792 }
00793 
00794 void QgsDbSourceSelect::setSearchExpression( const QString& regexp )
00795 {
00796 }
00797 
00798 void QgsGeomColumnTypeThread::setConnInfo( QString s )
00799 {
00800   mConnInfo = s;
00801 }
00802 
00803 void QgsGeomColumnTypeThread::addGeometryColumn( QString schema, QString table, QString column )
00804 {
00805   schemas.push_back( schema );
00806   tables.push_back( table );
00807   columns.push_back( column );
00808 }
00809 
00810 void QgsGeomColumnTypeThread::stop()
00811 {
00812   mStopped = true;
00813 }
00814 
00815 void QgsGeomColumnTypeThread::getLayerTypes()
00816 {
00817   mStopped = false;
00818 
00819   PGconn *pd = PQconnectdb( mConnInfo.toLocal8Bit() );
00820   if ( PQstatus( pd ) == CONNECTION_OK )
00821   {
00822     PQsetClientEncoding( pd, QString( "UNICODE" ).toLocal8Bit() );
00823 
00824     for ( uint i = 0; i < schemas.size(); i++ )
00825     {
00826       QString query = QgsDbSourceSelect::makeGeomQuery( schemas[i],
00827                       tables[i],
00828                       columns[i] );
00829       PGresult* gresult = PQexec( pd, query.toUtf8() );
00830       QString type;
00831       if ( PQresultStatus( gresult ) == PGRES_TUPLES_OK )
00832       {
00833         QStringList types;
00834 
00835         for ( int j = 0; j < PQntuples( gresult ); j++ )
00836         {
00837           QString type = QString::fromUtf8( PQgetvalue( gresult, j, 0 ) );
00838           if ( type != "" )
00839             types += type;
00840         }
00841 
00842         type = types.join( "," );
00843       }
00844       PQclear( gresult );
00845 
00846       if ( mStopped )
00847         break;
00848 
00849       // Now tell the layer list dialog box...
00850       emit setLayerType( schemas[i], tables[i], columns[i], type );
00851     }
00852   }
00853 
00854   PQfinish( pd );
00855 }

Generated on Tue Oct 28 16:51:26 2008 for Quantum GIS API Documentation by  doxygen 1.5.1