postgis_use_estimated_metadata.diff

Jeremy Palmer, 2010-03-08 04:39 PM

Download (17 KB)

View differences:

python/core/qgsdatasourceuri.sip (working copy)
60 60
  QString table() const;
61 61
  QString sql() const;
62 62
  QString geometryColumn() const;
63
  
64
  //! set use Estimated Metadata
65
  // added in 1.5
66
  void setUseEstimatedMetadata( bool theFlag );
67
  bool useEstimatedMetadata() const;
63 68

  
64 69
  // added in 1.1
65 70
  QString host() const;
src/app/postgres/qgspgnewconnection.cpp (working copy)
67 67
    // Ensure that cb_plublicSchemaOnly is set correctly
68 68
    on_cb_geometryColumnsOnly_clicked();
69 69

  
70
    s = Qt::Unchecked;
71
    if ( settings.value( key + "/estimatedMetadata", false ).toBool() )
72
      s = Qt::Checked;
73
    cb_useEstimatedMetadata->setCheckState( s );
74

  
70 75
    cbxSSLmode->setCurrentIndex( cbxSSLmode->findData( settings.value( key + "/sslmode", QgsDataSourceURI::SSLprefer ).toInt() ) );
71 76

  
72 77
    if ( settings.value( key + "/saveUsername" ).toString() == "true" )
......
132 137
  settings.setValue( baseKey + "/sslmode", cbxSSLmode->itemData( cbxSSLmode->currentIndex() ).toInt() );
133 138
  settings.setValue( baseKey + "/saveUsername", chkStoreUsername->isChecked() ? "true" : "false" );
134 139
  settings.setValue( baseKey + "/savePassword", chkStorePassword->isChecked() ? "true" : "false" );
140
  settings.setValue( baseKey + "/estimatedMetadata", cb_useEstimatedMetadata->isChecked() );
135 141

  
136 142
  // remove old save setting
137 143
  settings.remove( baseKey + "/save" );
src/app/postgres/qgspgsourceselect.cpp (working copy)
41 41
#include <pg_config.h>
42 42
#endif
43 43

  
44
// Note: Because the the geometry type select SQL is also in the qgspostgresprovider
45
// code this parameter is duplicated there. 
46
static const int sGeomTypeSelectLimit = 100;
47

  
44 48
QgsPgSourceSelect::QgsPgSourceSelect( QWidget *parent, Qt::WFlags fl )
45 49
    : QDialog( parent, fl ), mColumnTypeThread( NULL ), pd( 0 )
46 50
{
......
142 146
  settings.remove( key + "/sslmode" );
143 147
  settings.remove( key + "/publicOnly" );
144 148
  settings.remove( key + "/geometryColumnsOnly" );
149
  settings.remove( key + "/estimatedMetadata" );
145 150
  settings.remove( key + "/saveUsername" );
146 151
  settings.remove( key + "/savePassword" );
147 152
  settings.remove( key + "/save" );
......
352 357
    uri += QString( " key=\"%1\"" ).arg( pkColumnName );
353 358
  }
354 359

  
360
  if ( mURI.useEstimatedMetadata() )
361
  {
362
    uri += QString( " estimatedmetadata=true" );
363
  }
364

  
355 365
  uri += QString( " table=\"%1\".\"%2\" (%3) sql=%4" )
356 366
         .arg( schemaName ).arg( tableName )
357 367
         .arg( geomColumnName )
......
409 419
  QString username = settings.value( key + "/username" ).toString();
410 420
  QString password = settings.value( key + "/password" ).toString();
411 421

  
412
  QgsDataSourceURI uri;
413
  uri.setConnection( settings.value( key + "/host" ).toString(),
422
  mURI.setConnection( settings.value( key + "/host" ).toString(),
414 423
                     settings.value( key + "/port" ).toString(),
415 424
                     database,
416 425
                     username,
......
419 428

  
420 429
  bool searchPublicOnly = settings.value( key + "/publicOnly" ).toBool();
421 430
  bool searchGeometryColumnsOnly = settings.value( key + "/geometryColumnsOnly" ).toBool();
422

  
431
  mURI.setUseEstimatedMetadata( settings.value( key + "/estimatedMetadata" ).toBool() );
423 432
  // Need to escape the password to allow for single quotes and backslashes
424 433

  
425
  QgsDebugMsg( "Connection info: " + uri.connectionInfo() );
434
  QgsDebugMsg( "Connection info: " + mURI.connectionInfo() );
426 435

  
427
  m_connInfo = uri.connectionInfo();
436
  m_connInfo = mURI.connectionInfo();
428 437

  
429 438
  // Tidy up an existing connection if one exists.
430 439
  if ( pd != 0 )
......
562 571
  if ( mColumnTypeThread == NULL )
563 572
  {
564 573
    mColumnTypeThread = new QgsGeomColumnTypeThread();
565
    mColumnTypeThread->setConnInfo( m_privConnInfo );
574
    mColumnTypeThread->setConnInfo( m_privConnInfo, mURI );
566 575
  }
567 576
  mColumnTypeThread->addGeometryColumn( schema, table, column );
568 577
}
......
794 803
{
795 804
}
796 805

  
797
void QgsGeomColumnTypeThread::setConnInfo( QString s )
806
void QgsGeomColumnTypeThread::setConnInfo( QString s, QgsDataSourceURI& uri )
798 807
{
799 808
  mConnInfo = s;
809
  mUseEstimatedMetadata = uri.useEstimatedMetadata();
800 810
}
801 811

  
802 812
void QgsGeomColumnTypeThread::addGeometryColumn( QString schema, QString table, QString column )
......
828 838
                               " when geometrytype(%1) IN ('LINESTRING','MULTILINESTRING') THEN 'LINESTRING'"
829 839
                               " when geometrytype(%1) IN ('POLYGON','MULTIPOLYGON') THEN 'POLYGON'"
830 840
                               " end "
831
                               "from "
832
                               "\"%2\".\"%3\"" ).arg( "\"" + columns[i] + "\"" ).arg( schemas[i] ).arg( tables[i] );
841
                               "from " ).arg( "\"" + columns[i] + "\"" );
842
      if ( mUseEstimatedMetadata )
843
      {
844
        query += QString("(select %1 from %2 where %1 is not null limit %3) as t")
845
          .arg( "\"" + columns[i] + "\"" )
846
          .arg( "\"" + schemas[i] + "\".\"" + tables[i] + "\"" )
847
          .arg( sGeomTypeSelectLimit );
848
      }
849
      else
850
      {
851
        query += "\"" + schemas[i] + "\".\"" + tables[i] + "\"";
852
      }
853

  
833 854
      PGresult* gresult = PQexec( pd, query.toUtf8() );
834 855
      QString type;
835 856
      if ( PQresultStatus( gresult ) == PGRES_TUPLES_OK )
src/app/postgres/qgspgsourceselect.h (working copy)
22 22
#include "qgsdbfilterproxymodel.h"
23 23
#include "qgsdbtablemodel.h"
24 24
#include "qgscontexthelp.h"
25
#include "qgsdatasourceuri.h"
25 26

  
26 27
extern "C"
27 28
{
......
179 180
    // Our thread for doing long running queries
180 181
    QgsGeomColumnTypeThread* mColumnTypeThread;
181 182
    QString m_connInfo;
183
    QgsDataSourceURI mURI;
182 184
    QString m_privConnInfo;
183 185
    QStringList m_selectedTables;
184 186
    // Storage for the range of layer type icons
......
205 207
    Q_OBJECT
206 208
  public:
207 209

  
208
    void setConnInfo( QString s );
210
    void setConnInfo( QString s, QgsDataSourceURI& uri );
209 211
    void addGeometryColumn( QString schema, QString table, QString column );
210 212

  
211 213
    // These functions get the layer types and pass that information out
......
226 228

  
227 229
  private:
228 230
    QString mConnInfo;
231
    bool mUseEstimatedMetadata;
229 232
    bool mStopped;
230 233
    std::vector<QString> schemas, tables, columns;
231 234
};
src/core/qgsdatasourceuri.cpp (working copy)
23 23
#include <QStringList>
24 24
#include <QRegExp>
25 25

  
26
QgsDataSourceURI::QgsDataSourceURI() : mSSLmode( SSLprefer ), mKeyColumn( "" )
26
QgsDataSourceURI::QgsDataSourceURI() : mSSLmode( SSLprefer ), mKeyColumn( "" ), mUseEstimatedMetadata( false )
27 27
{
28 28
  // do nothing
29 29
}
30 30

  
31
QgsDataSourceURI::QgsDataSourceURI( QString uri ) : mSSLmode( SSLprefer ), mKeyColumn( "" )
31
QgsDataSourceURI::QgsDataSourceURI( QString uri ) : mSSLmode( SSLprefer ), mKeyColumn( "" ), mUseEstimatedMetadata( false )
32 32
{
33 33
  int i = 0;
34 34
  while ( i < uri.length() )
......
108 108
      {
109 109
        mKeyColumn = pval;
110 110
      }
111
      else if ( pname == "estimatedmetadata" )
112
      {
113
        if ( pval == "true" )
114
          mUseEstimatedMetadata = true;
115
        else
116
          mUseEstimatedMetadata = false;
117
      }
111 118
      else if ( pname == "service" )
112 119
      {
113 120
        QgsDebugMsg( "service keyword ignored" );
......
281 288
  mKeyColumn = column;
282 289
}
283 290

  
291

  
292
void QgsDataSourceURI::setUseEstimatedMetadata( bool theFlag )
293
{
294
  mUseEstimatedMetadata = theFlag;
295
}
296

  
297
bool QgsDataSourceURI::useEstimatedMetadata() const
298
{
299
  return mUseEstimatedMetadata;
300
}
301

  
284 302
void QgsDataSourceURI::setSql( QString sql )
285 303
{
286 304
  mSql = sql;
......
419 437
    theUri += QString( " key='%1'" ).arg( escape( mKeyColumn ) );
420 438
  }
421 439

  
440
  if ( mUseEstimatedMetadata == true )
441
  {
442
    theUri += QString(" estimatedmetadata=true" );
443
  }
444

  
422 445
  theUri += QString( " table=%1 (%2) sql=%3" )
423 446
            .arg( quotedTablename() )
424 447
            .arg( mGeometryColumn )
src/core/qgsdatasourceuri.h (working copy)
85 85
    QString sql() const;
86 86
    QString geometryColumn() const;
87 87

  
88
    //! set use Estimated Metadata
89
    // added in 1.5
90
    void setUseEstimatedMetadata( bool theFlag );
91
    bool useEstimatedMetadata() const;
92

  
88 93
    void clearSchema();
89 94
    void setSql( QString sql );
90 95

  
......
128 133
    enum SSLmode mSSLmode;
129 134
    //! key column
130 135
    QString mKeyColumn;
136
    //Use estimated metadata flag
137
    bool mUseEstimatedMetadata;
131 138
};
132 139

  
133 140
#endif //QGSDATASOURCEURI_H
src/providers/postgres/qgspostgresprovider.cpp (working copy)
49 49
const QString POSTGRES_KEY = "postgres";
50 50
const QString POSTGRES_DESCRIPTION = "PostgreSQL/PostGIS data provider";
51 51

  
52
// Note: Because the the geometry type select SQL is also in the qgspgsourceselect
53
// code this parameter is duplicated there. 
54
static const int sGeomTypeSelectLimit = 100;
55

  
52 56
QMap<QString, QgsPostgresProvider::Conn *> QgsPostgresProvider::Conn::connectionsRO;
53 57
QMap<QString, QgsPostgresProvider::Conn *> QgsPostgresProvider::Conn::connectionsRW;
54 58
QMap<QString, QString> QgsPostgresProvider::Conn::passwordCache;
......
59 63
    mFetching( false ),
60 64
    geomType( QGis::WKBUnknown ),
61 65
    mFeatureQueueSize( 200 ),
62
    mPrimaryKeyDefault( QString::null )
66
    mPrimaryKeyDefault( QString::null ),
67
    mIsDbPrimaryKey(false),
68
    mUseEstimatedMetadata(false)
63 69
{
64 70
  // assume this is a valid layer until we determine otherwise
65 71
  valid = true;
......
77 83
  geometryColumn = mUri.geometryColumn();
78 84
  sqlWhereClause = mUri.sql();
79 85
  primaryKey = mUri.keyColumn();
86
  mUseEstimatedMetadata = mUri.useEstimatedMetadata();
80 87

  
81 88
  // Keep a schema qualified table name for convenience later on.
82 89
  mSchemaTableName = mUri.quotedTablename();
......
1029 1036

  
1030 1037
QString QgsPostgresProvider::getPrimaryKey()
1031 1038
{
1039

  
1040
  // If we find a database primary key we will set this to true.  If it is a column which is serving
1041
  // as a primary key, then this will remain false.
1042

  
1043
  mIsDbPrimaryKey = false;
1044

  
1032 1045
  // check to see if there is an unique index on the relation, which
1033 1046
  // can be used as a key into the table. Primary keys are always
1034 1047
  // unique indices, so we catch them as well.
......
1188 1201
            log.append( tr( "The unique index on column '%1' is unsuitable because Qgis does not currently "
1189 1202
                            "support non-int4 type columns as a key into the table.\n" ).arg( columnName ) );
1190 1203
          else
1204
            mIsDbPrimaryKey = true;
1191 1205
            suitableKeyColumns.push_back( std::make_pair( columnName, columnType ) );
1192 1206
        }
1193 1207
        else
......
2647 2661

  
2648 2662
  sqlWhereClause = theSQL;
2649 2663

  
2650
  if ( !uniqueData( mSchemaName, mTableName, primaryKey ) )
2664
  if ( !mIsDbPrimaryKey && !uniqueData( mSchemaName, mTableName, primaryKey ) )
2651 2665
  {
2652 2666
    sqlWhereClause = prevWhere;
2653 2667
    return false;
......
2672 2686

  
2673 2687
  // First get an approximate count; then delegate to
2674 2688
  // a thread the task of getting the full count.
2689
  QString(sql);
2675 2690

  
2676 2691
#ifdef POSTGRESQL_THREADS
2677
  QString sql = QString( "select reltuples from pg_catalog.pg_class where relname=%1" ).arg( quotedValue( tableName ) );
2692
  sql = QString( "select reltuples::int from pg_catalog.pg_class where oid=regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
2678 2693
  QgsDebugMsg( "Running SQL: " + sql );
2679 2694
#else
2680
  QString sql = QString( "select count(*) from %1" ).arg( mSchemaTableName );
2681

  
2682
  if ( sqlWhereClause.length() > 0 )
2695
  if( mUseEstimatedMetadata )
2683 2696
  {
2684
    sql += " where " + sqlWhereClause;
2697
     sql = QString( "select reltuples::int from pg_catalog.pg_class where oid=regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
2685 2698
  }
2699
  else
2700
  {
2701
      sql = QString( "select count(*) from %1" ).arg( mSchemaTableName );
2702

  
2703
      if ( sqlWhereClause.length() > 0 )
2704
      {
2705
        sql += " where " + sqlWhereClause;
2706
      }
2707
  }
2686 2708
#endif
2687 2709

  
2688 2710
  Result result = connectionRO->PQexec( sql );
......
2746 2768
  QString ext;
2747 2769

  
2748 2770
  // get the extents
2749
  if ( sqlWhereClause.isEmpty() && !connectionRO->hasNoExtentEstimate() )
2771
  if ( ( mUseEstimatedMetadata || sqlWhereClause.isEmpty() ) && !connectionRO->hasNoExtentEstimate() )
2750 2772
  {
2751 2773
    result = connectionRO->PQexec( QString( "select estimated_extent(%1,%2,%3)" )
2752 2774
                                   .arg( quotedValue( mSchemaName ) )
......
2955 2977
                     " when geometrytype(%1) IN ('LINESTRING','MULTILINESTRING') THEN 'LINESTRING'"
2956 2978
                     " when geometrytype(%1) IN ('POLYGON','MULTIPOLYGON') THEN 'POLYGON'"
2957 2979
                     " end "
2958
                     "from %2" ).arg( quotedIdentifier( geometryColumn ) ).arg( mSchemaTableName );
2980
                     "from " ).arg( quotedIdentifier( geometryColumn ) );
2981
      if ( mUseEstimatedMetadata )
2982
      {
2983
        sql += QString("(select %1 from %2 where %1 is not null limit %3) as t")
2984
          .arg( quotedIdentifier( geometryColumn ) )
2985
          .arg( mSchemaTableName )
2986
          .arg( sGeomTypeSelectLimit );
2987
      }
2988
      else
2989
      {
2990
        sql += mSchemaTableName;
2991
      }
2992

  
2959 2993
      if ( mUri.sql() != "" )
2960 2994
        sql += " where " + mUri.sql();
2961 2995

  
src/providers/postgres/qgspostgresprovider.h (working copy)
408 408
     * the oid is used to fetch features.
409 409
     */
410 410
    QString primaryKey;
411
    bool mIsDbPrimaryKey;
411 412
    /**
412 413
     * Data type for the primary key
413 414
     */
......
461 462
    bool deduceEndian();
462 463
    bool getGeometryDetails();
463 464

  
465
    /* Use estimated metadata. Uses fast table counts, geometry type and extent determination */
466
    bool mUseEstimatedMetadata;
467

  
464 468
    // Produces a QMessageBox with the given title and text. Doesn't
465 469
    // return until the user has dismissed the dialog box.
466 470
    static void showMessageBox( const QString& title, const QString& text );
src/ui/qgspgnewconnectionbase.ui (working copy)
228 228
        </item>
229 229
       </layout>
230 230
      </item>
231
      <item row="4" column="0">
232
       <widget class="QCheckBox" name="cb_useEstimatedMetadata">
233
        <property name="toolTip">
234
         <string>Use estimated table statistics for the layer metadata.</string>
235
        </property>
236
        <property name="whatsThis">
237
         <string>&lt;html&gt;
238
&lt;body&gt;
239
&lt;p&gt;When the layer is setup various metadata is required for the PostGIS table. This includes information such as the table row count, geometry type and spatial extents of the data in the geometry column. If the table contains a large number of rows determining this metadata is time consuming.&lt;/p&gt;
240
&lt;p&gt;By activating this option the following fast table metadata operations are done:&lt;/p&gt;
241
&lt;p&gt;1) Row count is determined from table statistics obtained from running the PostgreSQL table analyse function.&lt;/p&gt;
242
&lt;p&gt;2) Table extents are always determined with the estimated_extent PostGIS function even if a layer filter is applied.&lt;/p&gt;
243
&lt;p&gt;3) If the table geometry type is unknown and is not exclusively taken from the geometry_columns table, then it is determined from the first 100 non-null geometry rows in the table.&lt;/p&gt;
244
&lt;/body&gt;
245
&lt;/html&gt;</string>
246
        </property>
247
        <property name="text">
248
         <string>Use estimated table metadata</string>
249
        </property>
250
       </widget>
251
      </item>
231 252
     </layout>
232 253
    </widget>
233 254
   </item>
......
246 267
  <tabstop>txtHost</tabstop>
247 268
  <tabstop>txtDatabase</tabstop>
248 269
  <tabstop>txtPort</tabstop>
270
  <tabstop>cbxSSLmode</tabstop>
249 271
  <tabstop>txtUsername</tabstop>
250 272
  <tabstop>txtPassword</tabstop>
273
  <tabstop>chkStoreUsername</tabstop>
274
  <tabstop>chkStorePassword</tabstop>
251 275
  <tabstop>cb_geometryColumnsOnly</tabstop>
252 276
  <tabstop>cb_publicSchemaOnly</tabstop>
277
  <tabstop>cb_useEstimatedMetadata</tabstop>
278
  <tabstop>btnConnect</tabstop>
253 279
  <tabstop>buttonBox</tabstop>
254 280
 </tabstops>
255 281
 <resources/>