Skip to content

Commit c5cce4b

Browse files
committedSep 4, 2015
postgres provider: add support for compound keys on views
1 parent e3b9a98 commit c5cce4b

File tree

4 files changed

+155
-43
lines changed

4 files changed

+155
-43
lines changed
 

‎src/providers/postgres/qgspgsourceselect.cpp

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,20 @@ QWidget *QgsPgSourceSelectDelegate::createEditor( QWidget *parent, const QStyleO
7676
if ( values.size() > 0 )
7777
{
7878
QComboBox *cb = new QComboBox( parent );
79-
cb->addItems( values );
79+
80+
QStandardItemModel *model = new QStandardItemModel( values.size(), 1, cb );
81+
82+
int row = 0;
83+
foreach ( QString value, values )
84+
{
85+
QStandardItem *item = new QStandardItem( value );
86+
item->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
87+
item->setData( Qt::Unchecked, Qt::CheckStateRole );
88+
model->setItem( row++, 0, item );
89+
}
90+
91+
cb->setModel( model );
92+
8093
return cb;
8194
}
8295
}
@@ -101,8 +114,25 @@ void QgsPgSourceSelectDelegate::setEditorData( QWidget *editor, const QModelInde
101114
if ( index.column() == QgsPgTableModel::dbtmType )
102115
cb->setCurrentIndex( cb->findData( index.data( Qt::UserRole + 2 ).toInt() ) );
103116

104-
if ( index.column() == QgsPgTableModel::dbtmPkCol && !index.data( Qt::UserRole + 2 ).toString().isEmpty() )
105-
cb->setCurrentIndex( cb->findText( index.data( Qt::UserRole + 2 ).toString() ) );
117+
if ( index.column() == QgsPgTableModel::dbtmPkCol && !index.data( Qt::UserRole + 2 ).toStringList().isEmpty() )
118+
{
119+
QStringList cols = index.data( Qt::UserRole + 2 ).toStringList();
120+
121+
foreach ( QString col, cols )
122+
{
123+
QStandardItemModel *cbm = qobject_cast<QStandardItemModel*>( cb->model() );
124+
for ( int idx = 0; idx < cbm->rowCount(); idx++ )
125+
{
126+
QStandardItem *item = cbm->item( idx, 0 );
127+
if ( item->text() != col )
128+
continue;
129+
130+
item->setData( Qt::Checked, Qt::CheckStateRole );
131+
break;
132+
}
133+
}
134+
135+
}
106136
}
107137

108138
QLineEdit *le = qobject_cast<QLineEdit*>( editor );
@@ -132,9 +162,17 @@ void QgsPgSourceSelectDelegate::setModelData( QWidget *editor, QAbstractItemMode
132162
}
133163
else if ( index.column() == QgsPgTableModel::dbtmPkCol )
134164
{
135-
QString value( cb->currentText() );
136-
model->setData( index, value.isEmpty() ? tr( "Select..." ) : value );
137-
model->setData( index, value, Qt::UserRole + 2 );
165+
QStandardItemModel *cbm = qobject_cast<QStandardItemModel*>( cb->model() );
166+
QStringList cols;
167+
for ( int idx = 0; idx < cbm->rowCount(); idx++ )
168+
{
169+
QStandardItem *item = cbm->item( idx, 0 );
170+
if ( item->data( Qt::CheckStateRole ) == Qt::Checked )
171+
cols << item->text();
172+
}
173+
174+
model->setData( index, cols.isEmpty() ? tr( "Select..." ) : cols.join( ", " ) );
175+
model->setData( index, cols, Qt::UserRole + 2 );
138176
}
139177
}
140178

@@ -196,7 +234,7 @@ QgsPgSourceSelect::QgsPgSourceSelect( QWidget *parent, Qt::WindowFlags fl, bool
196234
mSearchColumnComboBox->addItem( tr( "Table" ) );
197235
mSearchColumnComboBox->addItem( tr( "Type" ) );
198236
mSearchColumnComboBox->addItem( tr( "Geometry column" ) );
199-
mSearchColumnComboBox->addItem( tr( "Primary key column" ) );
237+
mSearchColumnComboBox->addItem( tr( "Feature id" ) );
200238
mSearchColumnComboBox->addItem( tr( "SRID" ) );
201239
mSearchColumnComboBox->addItem( tr( "Sql" ) );
202240

@@ -378,7 +416,7 @@ void QgsPgSourceSelect::on_mSearchColumnComboBox_currentIndexChanged( const QStr
378416
{
379417
mProxyModel.setFilterKeyColumn( QgsPgTableModel::dbtmGeomCol );
380418
}
381-
else if ( text == tr( "Primary key column" ) )
419+
else if ( text == tr( "Feature id" ) )
382420
{
383421
mProxyModel.setFilterKeyColumn( QgsPgTableModel::dbtmPkCol );
384422
}

‎src/providers/postgres/qgspgtablemodel.cpp

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ QgsPgTableModel::QgsPgTableModel()
3232
headerLabels << tr( "Data Type" );
3333
headerLabels << tr( "Spatial Type" );
3434
headerLabels << tr( "SRID" );
35-
headerLabels << tr( "Primary Key" );
35+
headerLabels << tr( "Feature id" );
3636
headerLabels << tr( "Select at id" );
3737
headerLabels << tr( "Sql" );
3838
setHorizontalHeaderLabels( headerLabels );
@@ -62,15 +62,15 @@ void QgsPgTableModel::addTableEntry( const QgsPostgresLayerProperty& layerProper
6262
QString tip;
6363
if ( wkbType == QGis::WKBUnknown )
6464
{
65-
tip = tr( "Specify a geometry type" );
65+
tip = tr( "Specify a geometry type in the '%1' column" ).arg( tr( "Data Type" ) );
6666
}
6767
else if ( wkbType != QGis::WKBNoGeometry && srid == INT_MIN )
6868
{
69-
tip = tr( "Enter a SRID" );
69+
tip = tr( "Enter a SRID into the '%1' column" ).arg( tr( "SRID" ) );
7070
}
7171
else if ( layerProperty.pkCols.size() > 0 )
7272
{
73-
tip = tr( "Select a primary key" );
73+
tip = tr( "Select columns in the '%1' column that uniquely identify features of this layer" ).arg( tr( "Feature id" ) );
7474
}
7575

7676
QStandardItem *schemaNameItem = new QStandardItem( layerProperty.schemaName );
@@ -127,16 +127,18 @@ void QgsPgTableModel::addTableEntry( const QgsPostgresLayerProperty& layerProper
127127
{
128128
if ( tip.isEmpty() )
129129
{
130-
item->setFlags( item->flags() | Qt::ItemIsSelectable | Qt::ItemIsEnabled );
130+
item->setFlags( item->flags() | Qt::ItemIsSelectable );
131131
item->setToolTip( "" );
132132
}
133133
else
134134
{
135135
item->setFlags( item->flags() & ~Qt::ItemIsSelectable );
136136

137+
if ( item == schemaNameItem )
138+
item->setData( QgsApplication::getThemeIcon( "/mIconWarn.png" ), Qt::DecorationRole );
139+
137140
if ( item == schemaNameItem || item == tableItem || item == geomItem )
138141
{
139-
item->setFlags( item->flags() & ~Qt::ItemIsEnabled );
140142
item->setToolTip( tip );
141143
}
142144
}
@@ -267,38 +269,49 @@ bool QgsPgTableModel::setData( const QModelIndex &idx, const QVariant &value, in
267269
QString tip;
268270
if ( wkbType == QGis::WKBUnknown )
269271
{
270-
tip = tr( "Specify a geometry type" );
272+
tip = tr( "Specify a geometry type in the '%1' column" ).arg( tr( "Data Type" ) );
271273
}
272274
else if ( wkbType != QGis::WKBNoGeometry )
273275
{
274276
bool ok;
275277
int srid = idx.sibling( idx.row(), dbtmSrid ).data().toInt( &ok );
276278

277279
if ( !ok || srid == INT_MIN )
278-
tip = tr( "Enter a SRID" );
280+
tip = tr( "Enter a SRID into the '%1' column" ).arg( tr( "SRID" ) );
279281
}
280282

281283
QStringList pkCols = idx.sibling( idx.row(), dbtmPkCol ).data( Qt::UserRole + 1 ).toStringList();
282284
if ( tip.isEmpty() && pkCols.size() > 0 )
283285
{
284-
if ( !pkCols.contains( idx.sibling( idx.row(), dbtmPkCol ).data().toString() ) )
285-
tip = tr( "Select a primary key" );
286+
QSet<QString> s0( idx.sibling( idx.row(), dbtmPkCol ).data( Qt::UserRole + 2 ).toStringList().toSet() );
287+
QSet<QString> s1( pkCols.toSet() );
288+
if ( s0.intersect( s1 ).isEmpty() )
289+
tip = tr( "Select columns in the '%1' column that uniquely identify features of this layer" ).arg( tr( "Feature id" ) );
286290
}
287291

288292
for ( int i = 0; i < dbtmColumns; i++ )
289293
{
290294
QStandardItem *item = itemFromIndex( idx.sibling( idx.row(), i ) );
291295
if ( tip.isEmpty() )
292296
{
293-
item->setFlags( item->flags() | Qt::ItemIsSelectable | Qt::ItemIsEnabled );
297+
if ( i == dbtmSchema )
298+
{
299+
item->setData( QVariant(), Qt::DecorationRole );
300+
}
301+
302+
item->setFlags( item->flags() | Qt::ItemIsSelectable );
294303
item->setToolTip( "" );
295304
}
296305
else
297306
{
298307
item->setFlags( item->flags() & ~Qt::ItemIsSelectable );
308+
309+
if ( i == dbtmSchema )
310+
item->setData( QgsApplication::getThemeIcon( "/mIconWarn.png" ), Qt::DecorationRole );
311+
299312
if ( i == dbtmSchema || i == dbtmTable || i == dbtmGeomCol )
300313
{
301-
item->setFlags( item->flags() & ~Qt::ItemIsEnabled );
314+
item->setFlags( item->flags() );
302315
item->setToolTip( tip );
303316
}
304317
}
@@ -325,10 +338,9 @@ QString QgsPgTableModel::layerURI( const QModelIndex &index, const QString& conn
325338
}
326339

327340
QStandardItem *pkItem = itemFromIndex( index.sibling( index.row(), dbtmPkCol ) );
328-
QString pkColumnName = pkItem->data( Qt::UserRole + 2 ).toString();
329-
330-
if ( pkItem->data( Qt::UserRole + 1 ).toStringList().size() > 0 &&
331-
!pkItem->data( Qt::UserRole + 1 ).toStringList().contains( pkColumnName ) )
341+
QSet<QString> s0( pkItem->data( Qt::UserRole + 1 ).toStringList().toSet() );
342+
QSet<QString> s1( pkItem->data( Qt::UserRole + 2 ).toStringList().toSet() );
343+
if ( !s0.isEmpty() && s0.intersect( s1 ).isEmpty() )
332344
{
333345
// no valid primary candidate selected
334346
QgsDebugMsg( "no pk candidate selected" );
@@ -358,7 +370,14 @@ QString QgsPgTableModel::layerURI( const QModelIndex &index, const QString& conn
358370
QString sql = index.sibling( index.row(), dbtmSql ).data( Qt::DisplayRole ).toString();
359371

360372
QgsDataSourceURI uri( connInfo );
361-
uri.setDataSource( schemaName, tableName, geomColumnName, sql, pkColumnName );
373+
374+
QStringList cols;
375+
foreach ( QString col, s1 )
376+
{
377+
cols << QgsPostgresConn::quotedIdentifier( col );
378+
}
379+
380+
uri.setDataSource( schemaName, tableName, geomColumnName, sql, cols.join( "," ) );
362381
uri.setUseEstimatedMetadata( useEstimatedMetadata );
363382
uri.setWkbType( wkbType );
364383
uri.setSrid( srid );

‎src/providers/postgres/qgspostgresprovider.cpp

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,14 +1180,69 @@ bool QgsPostgresProvider::determinePrimaryKey()
11801180

11811181
if ( !primaryKey.isEmpty() )
11821182
{
1183-
int idx = fieldNameIndex( primaryKey );
1183+
QStringList cols;
11841184

1185-
if ( idx >= 0 )
1185+
// remove quotes from key list
1186+
if ( primaryKey.startsWith( '"' ) && primaryKey.endsWith( '"' ) )
1187+
{
1188+
int i = 1;
1189+
QString col;
1190+
while ( i < primaryKey.size() )
1191+
{
1192+
if ( primaryKey[i] == '"' )
1193+
{
1194+
if ( i + 1 < primaryKey.size() && primaryKey[i+1] == '"' )
1195+
{
1196+
i++;
1197+
}
1198+
else
1199+
{
1200+
cols << col;
1201+
col = "";
1202+
1203+
if ( ++i == primaryKey.size() )
1204+
break;
1205+
1206+
Q_ASSERT( primaryKey[i] == ',' );
1207+
i++;
1208+
Q_ASSERT( primaryKey[i] == '"' );
1209+
i++;
1210+
col = "";
1211+
continue;
1212+
}
1213+
}
1214+
1215+
col += primaryKey[i++];
1216+
}
1217+
}
1218+
else if ( primaryKey.contains( "," ) )
1219+
{
1220+
cols = primaryKey.split( "," );
1221+
}
1222+
else
1223+
{
1224+
cols << primaryKey;
1225+
primaryKey = quotedIdentifier( primaryKey );
1226+
}
1227+
1228+
foreach ( QString col, cols )
1229+
{
1230+
int idx = fieldNameIndex( col );
1231+
if ( idx < 0 )
1232+
{
1233+
QgsMessageLog::logMessage( tr( "Key field '%1' for view not found." ).arg( col ), tr( "PostGIS" ) );
1234+
mPrimaryKeyAttrs.clear();
1235+
break;
1236+
}
1237+
1238+
mPrimaryKeyAttrs << idx;
1239+
}
1240+
1241+
if ( mPrimaryKeyAttrs.size() > 0 )
11861242
{
11871243
if ( mUseEstimatedMetadata || uniqueData( mQuery, primaryKey ) )
11881244
{
1189-
mPrimaryKeyType = ( mAttributeFields[idx].type() == QVariant::Int || mAttributeFields[idx].type() == QVariant::LongLong ) ? pktInt : pktFidMap;
1190-
mPrimaryKeyAttrs << idx;
1245+
mPrimaryKeyType = ( mPrimaryKeyAttrs.size() == 1 && ( mAttributeFields[ mPrimaryKeyAttrs[0] ].type() == QVariant::Int || mAttributeFields[ mPrimaryKeyAttrs[0] ].type() == QVariant::LongLong ) ) ? pktInt : pktFidMap;
11911246
}
11921247
else
11931248
{
@@ -1196,7 +1251,7 @@ bool QgsPostgresProvider::determinePrimaryKey()
11961251
}
11971252
else
11981253
{
1199-
QgsMessageLog::logMessage( tr( "Key field '%1' for view not found." ).arg( primaryKey ), tr( "PostGIS" ) );
1254+
QgsMessageLog::logMessage( tr( "Keys for view undefined." ).arg( primaryKey ), tr( "PostGIS" ) );
12001255
}
12011256
}
12021257
else
@@ -1269,12 +1324,12 @@ bool QgsPostgresProvider::determinePrimaryKey()
12691324
return mValid;
12701325
}
12711326

1272-
bool QgsPostgresProvider::uniqueData( QString query, QString colName )
1327+
bool QgsPostgresProvider::uniqueData( QString query, QString quotedColName )
12731328
{
12741329
Q_UNUSED( query );
12751330
// Check to see if the given column contains unique data
12761331
QString sql = QString( "SELECT count(distinct %1)=count(%1) FROM %2%3" )
1277-
.arg( quotedIdentifier( colName ) )
1332+
.arg( quotedColName )
12781333
.arg( mQuery )
12791334
.arg( filterWhereClause() );
12801335

@@ -3062,16 +3117,16 @@ QgsVectorLayerImport::ImportError QgsPostgresProvider::createEmptyLayer(
30623117
}
30633118
else
30643119
{
3065-
// if the pk field's type is one of the postgres integer types,
3066-
// use the equivalent autoincremental type (serialN)
3067-
if ( primaryKeyType == "int2" || primaryKeyType == "int4" )
3068-
{
3069-
primaryKeyType = "serial";
3070-
}
3071-
else if ( primaryKeyType == "int8" )
3072-
{
3073-
primaryKeyType = "serial8";
3074-
}
3120+
// if the pk field's type is one of the postgres integer types,
3121+
// use the equivalent autoincremental type (serialN)
3122+
if ( primaryKeyType == "int2" || primaryKeyType == "int4" )
3123+
{
3124+
primaryKeyType = "serial";
3125+
}
3126+
else if ( primaryKeyType == "int8" )
3127+
{
3128+
primaryKeyType = "serial8";
3129+
}
30753130
}
30763131

30773132
try

‎src/ui/qgsdbsourceselectbase.ui

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<rect>
77
<x>0</x>
88
<y>0</y>
9-
<width>592</width>
9+
<width>773</width>
1010
<height>476</height>
1111
</rect>
1212
</property>

0 commit comments

Comments
 (0)