Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport release-3_14] [postgres] Fix superslow layer instantiation in huge databases by only fetching type info of used fields. #38181

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
68 changes: 44 additions & 24 deletions src/providers/postgres/qgspostgresprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,42 +837,23 @@ bool QgsPostgresProvider::loadFields()
// Clear cached information about enum values support
mShared->clearSupportsEnumValuesCache();

QString sql;
QString attroidsFilter;

if ( !mIsQuery )
{
QgsDebugMsgLevel( QStringLiteral( "Loading fields for table %1" ).arg( mTableName ), 2 );

// Get the relation oid for use in later queries
QString sql = QStringLiteral( "SELECT regclass(%1)::oid" ).arg( quotedValue( mQuery ) );
QgsPostgresResult tresult( connectionRO()->PQexec( sql ) );
QString tableoid = tresult.PQgetvalue( 0, 0 );

// Get the table description
sql = QStringLiteral( "SELECT description FROM pg_description WHERE objoid=%1 AND objsubid=0" ).arg( tableoid );
tresult = connectionRO()->PQexec( sql );
sql = QStringLiteral( "SELECT description FROM pg_description WHERE objoid=regclass(%1)::oid AND objsubid=0" ).arg( quotedValue( mQuery ) );
QgsPostgresResult tresult( connectionRO()->PQexec( sql ) );
if ( tresult.PQntuples() > 0 )
{
mDataComment = tresult.PQgetvalue( 0, 0 );
mLayerMetadata.setAbstract( mDataComment );
}
}

// Collect type info
QString sql = QStringLiteral( "SELECT oid,typname,typtype,typelem,typlen FROM pg_type" );
QgsPostgresResult typeResult( connectionRO()->PQexec( sql ) );

QMap<Oid, PGTypeInfo> typeMap;
for ( int i = 0; i < typeResult.PQntuples(); ++i )
{
PGTypeInfo typeInfo =
{
/* typeName = */ typeResult.PQgetvalue( i, 1 ),
/* typeType = */ typeResult.PQgetvalue( i, 2 ),
/* typeElem = */ typeResult.PQgetvalue( i, 3 ),
/* typeLen = */ typeResult.PQgetvalue( i, 4 ).toInt()
};
typeMap.insert( typeResult.PQgetvalue( i, 0 ).toUInt(), typeInfo );
}

// Populate the field vector for this layer. The field vector contains
// field name, type, length, and precision (if numeric)
sql = QStringLiteral( "SELECT * FROM %1 LIMIT 0" ).arg( mQuery );
Expand All @@ -884,6 +865,14 @@ bool QgsPostgresProvider::loadFields()
QMap<Oid, QMap<int, bool> > notNullMap, uniqueMap;
if ( result.PQnfields() > 0 )
{
// Collect attribiute oids
QSet<Oid> attroids;
for ( int i = 0; i < result.PQnfields(); i++ )
{
Oid attroid = result.PQftype( i );
attroids.insert( attroid );
}

// Collect table oids
QSet<Oid> tableoids;
for ( int i = 0; i < result.PQnfields(); i++ )
Expand Down Expand Up @@ -948,10 +937,41 @@ bool QgsPostgresProvider::loadFields()
uniqueMap[attrelid][attnum] = uniqueConstraint;
identityMap[attrelid][attnum] = attIdentity.isEmpty() ? " " : attIdentity;
generatedMap[attrelid][attnum] = attGenerated.isEmpty() ? "" : defVal;

// Also include atttype oid from pg_attribute, because PQnfields only returns basic type for for domains
attroids.insert( attType );
}
}

// Prepare filter for fetching pg_type info
if ( !attroids.isEmpty() )
{
QStringList attroidsList;
for ( Oid attroid : qgis::as_const( attroids ) )
{
attroidsList.append( QString::number( attroid ) );
}
attroidsFilter = QStringLiteral( "WHERE oid in (%1)" ).arg( attroidsList.join( ',' ) );
}
}

// Collect type info
sql = QStringLiteral( "SELECT oid,typname,typtype,typelem,typlen FROM pg_type %1" ).arg( attroidsFilter );
QgsPostgresResult typeResult( connectionRO()->PQexec( sql ) );

QMap<Oid, PGTypeInfo> typeMap;
for ( int i = 0; i < typeResult.PQntuples(); ++i )
{
PGTypeInfo typeInfo =
{
/* typeName = */ typeResult.PQgetvalue( i, 1 ),
/* typeType = */ typeResult.PQgetvalue( i, 2 ),
/* typeElem = */ typeResult.PQgetvalue( i, 3 ),
/* typeLen = */ typeResult.PQgetvalue( i, 4 ).toInt()
};
typeMap.insert( typeResult.PQgetvalue( i, 0 ).toUInt(), typeInfo );
}

QSet<QString> fields;
mAttributeFields.clear();
mIdentityFields.clear();
Expand Down