2.QgsPostgresProvider类
class QgsPostgresProvider final: public QgsVectorDataProvider
{
};
部分成员变量:
- QgsFields mAttributeFields;
保存属性字段信息,该信息在loadFields()内被填充。 - QgsDataSourceUri mUri;
保存传入的数据源uri。 - QString mQuery;
保存查询源,可能是表名或子查询。 - QString mSqlWhereClause;
给定了sql的通用where条件。比如:WHERE schema=‘sde’。 - mutable QgsRectangle mLayerExtent;
保存了图层的范围。 - QgsWkbTypes::Type mRequestedGeomType = QgsWkbTypes::Unknown ;
- QgsWkbTypes::Type mDetectedGeomType = QgsWkbTypes::Unknown ;
requested表示在uri中指定的几何类型,detected表示从数据库中探测出的几何类型。 - QString mDetectedSrid;
- QString mRequestedSrid;
空间参考下的srid。 - QgsVectorDataProvider::Capabilities mEnabledCapabilities = QgsVectorDataProvider::Capabilities();
保存了该Providers所具有的能力。 - mConnectionRO
保存只读的数据库连接,在构造函数时即尝试获取一个数据库连接。 - mConnectionRW
保存读写的数据库连接,在connectionRW()被调用时,判断该变量是否为空,为空则获取一个数据库连接。
(1) QgsPostgresProvider()
QgsPostgresProvider::QgsPostgresProvider( QString const &uri, const ProviderOptions &options,
QgsDataProvider::ReadFlags flags )
: QgsVectorDataProvider( uri, options, flags )
, mShared( new QgsPostgresSharedData )
{
......
// 1)
mUri = QgsDataSourceUri( uri );
// populate members from the uri structure
mSchemaName = mUri.schema();
mTableName = mUri.table();
mGeometryColumn = mUri.geometryColumn();
mBoundingBoxColumn = mUri.param( "bbox" );
......
// 2)
if ( mSchemaName.isEmpty() && mTableName.startsWith( '(' ) && mTableName.endsWith( ')' ) )
{
mIsQuery = true;
setQuery( mTableName );
mTableName.clear();
}
else
{
mIsQuery = false;
setQuery( ( !mSchemaName.isEmpty() ? quotedIdentifier( mSchemaName ) + '.' : QString() )
+ ( !mTableName.isEmpty() ? quotedIdentifier( mTableName ) : QString() ) );
}
......
if ( !hasSufficientPermsAndCapabilities() ) // check permissions and set capabilities
{
disconnectDb();
return;
}
……
// 3)
if ( !hasSufficientPermsAndCapabilities() ) // check permissions and set capabilities
{
disconnectDb();
return;
}
// 4)
if ( !getGeometryDetails() ) // gets srid, geometry and data type
{
// the table is not a geometry table
QgsMessageLog::logMessage( tr( "Invalid PostgreSQL layer" ), tr( "PostGIS" ) );
disconnectDb();
return;
}
……
}
构造函数
- 首先从uri里提取出表名、几何列等基本信息放到成员变量里。
- 然后提取schema,表名等,通过setQuery()设置查询源,如:schema为sde,表名为testTable,则查询源为"sde"."testTable",该值由成员变量mQuery保存,后续的sql语句将直接使用该变量值。
- 通过hasSufficientPermsAndCapabilities()判断数据库增删改查等权限并将权限赋值给mEnabledCapabilities。
- 从数据库查询srid,geometry等基本信息。
(2) featureSource() override
QgsAbstractFeatureSource *QgsPostgresProvider::featureSource() const
{
return new QgsPostgresFeatureSource( this );
}
featureSource()返回new的QgsPostgresFeatureSource对象,QgsPostgresFeatureSource类保存了要素的一些源信息,如:几何列、属性字段、srid等,在QgsPostgresFeatureSource构造的时候,会将QgsPostgresProvider的一些基本信息赋值给QgsPostgresFeatureSource。
(3) storageType() override
QString QgsPostgresProvider::storageType() const
{
return QStringLiteral( "PostgreSQL database with PostGIS extension" );
}
storageType()返回储存类型,其是一个字符串描述。
(4) crs() override
QgsCoordinateReferenceSystem QgsPostgresProvider::crs() const
{
QgsCoordinateReferenceSystem srs;
int srid = mRequestedSrid.isEmpty() ? mDetectedSrid.toInt() : mRequestedSrid.toInt();
return sridToCrs( srid, connectionRO() );
}
crs()获取坐标参考系,QgsPostgresProvider内部有两个变量,mRequestedSrid:通过uri传入的srid;mDetectedSrid:从数据库或数据源检测出的srid。优先使用mRequestedSrid获取具体的坐标参考系对象。
(5) getFeatures() override
QgsFeatureIterator QgsPostgresProvider::getFeatures( const QgsFeatureRequest &request ) const
{
……
QgsPostgresFeatureSource *featureSrc = static_cast<QgsPostgresFeatureSource *>( featureSource() );
return QgsFeatureIterator( new QgsPostgresFeatureIterator( featureSrc, true, request ) );
}
getFeatures()返回feature迭代器,通过迭代器遍历feature。QgsFeatureIterator有一个QgsAbstractFeatureIterator类型的指针成员变量mIter,QgsFeatureIteratord的接口最终是调用了mIter的函数。而具体的函数实现由QgsPostgresFeatureIterator实现。
(6) wkbType() override
QgsWkbTypes::Type QgsPostgresProvider::wkbType() const
{
return mRequestedGeomType != QgsWkbTypes::Unknown ? mRequestedGeomType : mDetectedGeomType;
}
wkbType()返回feature的类型,类型声明在QgsWkbTypes::Type中。
(7) layerMetadata() override
QgsLayerMetadata QgsPostgresProvider::layerMetadata() const
{
return mLayerMetadata;
}
layerMetadata()返回QgsLayerMetadata类型的成员变量mLayerMetadata。QgsLayerMetadata类为地图图层储存了一些结构化的元数据。
(8) featureCount() override
long long QgsPostgresProvider::featureCount() const
{
long long featuresCounted = mShared->featuresCounted();
if ( featuresCounted >= 0 )
return featuresCounted;
……
// 2)
if ( !mIsQuery && mUseEstimatedMetadata )
{
if ( relkind() == Relkind::View && connectionRO()->pgVersion() >= 90000 )
{
sql = QStringLiteral( "EXPLAIN (FORMAT JSON) SELECT 1 FROM %1%2" ).arg( mQuery, filterWhereClause() );
……
}
else
{
sql = QStringLiteral( "SELECT reltuples::bigint FROM pg_catalog.pg_class WHERE oid=regclass(%1)::oid" ).arg( quotedValue( mQuery ) );
QgsPostgresResult result( connectionRO()->PQexec( sql ) );
num = result.PQgetvalue( 0, 0 ).toLongLong();
}
}
else
{
sql = QStringLiteral( "SELECT count(*) FROM %1%2" ).arg( mQuery, filterWhereClause() );
QgsPostgresResult result( connectionRO()->PQexec( sql ) );
num = result.PQgetvalue( 0, 0 ).toLongLong();
}
mShared->setFeaturesCounted( num );
}
featureCount()返回要素数量。
- 从mShared中获取要素数量,mShared为std::shared_ptr<QgsPostgresSharedData> 类型。
- 如果mShared获取不到要素数量,则从数据库中查询要素数量,并赋值给mShared。
(9) empty() override
bool QgsPostgresProvider::empty() const
{
QString sql = QStringLiteral( "SELECT EXISTS (SELECT * FROM %1%2 LIMIT 1)" ).arg( mQuery, filterWhereClause() );
QgsPostgresResult res( connectionRO()->PQexec( sql ) );
if ( res.PQresultStatus() != PGRES_TUPLES_OK )
{
pushError( res.PQresultErrorMessage() );
return false;
}
return res.PQgetvalue( 0, 0 ) != QLatin1String( "t" );
}
empty()从数据库查询是否至少有一个要素。
(10) extent() override
QgsRectangle QgsPostgresProvider::extent() const
{
if ( !isValid() || mGeometryColumn.isNull() )
return QgsRectangle();
if ( mSpatialColType == SctGeography )
return QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
if ( mLayerExtent.isEmpty() )
{
……
// get the extents
if ( !mIsQuery && mUseEstimatedMetadata )
{
……
sql = QStringLiteral( "SELECT %1(%2,%3,%4)" )
.arg( connectionRO()->majorVersion() < 2 ? "estimated_extent" :
( connectionRO()->majorVersion() == 2 && connectionRO()->minorVersion() < 1 ? "st_estimated_extent" : "st_estimatedextent" ),
quotedValue( mSchemaName ),
quotedValue( mTableName ),
quotedValue( mGeometryColumn ) );
……
}
if ( ext.isEmpty() )
{
sql = QStringLiteral( "SELECT %1(%2%3) FROM %4%5" )
.arg( connectionRO()->majorVersion() < 2 ? "extent" : "st_extent",
quotedIdentifier( mBoundingBoxColumn ),
mSpatialColType == SctPcPatch ? "::geometry" : "",
mQuery,
filterWhereClause() );
……
}
}
extent()返回范围,在成员变量mLayerExtent为空时,则从数据库查找范围。
(11) fields() override
QgsFields QgsPostgresProvider::fields() const
{
return mAttributeFields;
}
fields()返回属性字段,该数据由QgsFields类型的成员变量mAttributeFields储存。QgsFields类可以添加QgsField对象,每个对象即代表一个属性字段,QgsField可以设置属性名setName()、类型setType()、类型名setTypeName()、长度setLength()、精度setPrecision()、注释setComment()。
(12) addFeatures() override
bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags )
{
……
// Optimization: if we have a single primary key column whose default value
// is a sequence, and that none of the features have a value set for that
// column, then we can completely omit inserting it.
bool skipSinglePKField = false;
bool overrideIdentity = false;
// 1)
if ( ( mPrimaryKeyType == PktInt || mPrimaryKeyType == PktInt64 || mPrimaryKeyType == PktFidMap || mPrimaryKeyType == PktUint64 ) )
{
……
for ( int i = 0; i < flist.size(); i++ )
{
QgsAttributes attrs2 = flist[i].attributes();
QVariant v2 = attrs2.value( idx, QVariant( QVariant::Int ) );
// a PK field with a sequence val is auto populate by QGIS with this default
// we are only interested in non default values
if ( !v2.isNull() && v2.toString() != defaultValue )
{
foundNonEmptyPK = true;
break;
}
} //for
skipSinglePKField = !foundNonEmptyPK;
……
}
// 2)
QgsAttributes attributevec = flist[0].attributes();
// look for unique attribute values to place in statement instead of passing as parameter
for ( int idx = 0; idx < attributevec.count(); ++idx )
{……}
insert += QStringLiteral( ") %1VALUES (%2)" ).arg( overrideIdentity ? "OVERRIDING SYSTEM VALUE " : "" ).arg( values );
……
// 3)
for ( QgsFeatureList::iterator features = flist.begin(); features != flist.end(); ++features )
{……}
……
}
addFeatures()添加要素。
- 从flist中判断每个要素的主键属性,如果有值且非默认值,则认为有主键值,skipSinglePKField置false,否则置true。skipSinglePKField:当只有单主键时,如果主键是序列,那么插入要素时可以考虑忽略主键。
- 遍历第一个要素的所有属性,组装完Insert语句的字段。
- 遍历要素,获取每个要素的属性值,组装Values。
- 执行INSERT语句,将要素入库。
(13) deleteFeatures() override
bool QgsPostgresProvider::deleteFeatures( const QgsFeatureIds &ids )
{}
deleteFeature()从数据库删除要素。
(14) addAttributes() override
bool QgsPostgresProvider::addAttributes( const QList<QgsField> &attributes )
{
……
QString sql = QStringLiteral( "ALTER TABLE %1 " ).arg( mQuery );
for ( QList<QgsField>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter )
{
……
sql.append( QStringLiteral( "%1ADD COLUMN %2 %3" ).arg( delim, quotedIdentifier( iter->name() ), type ) );
……
}
……
for ( QList<QgsField>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter )
{
if ( !iter->comment().isEmpty() )
{
sql = QStringLiteral( "COMMENT ON COLUMN %1.%2 IS %3" )
.arg( mQuery,
quotedIdentifier( iter->name() ),
quotedValue( iter->comment() ) );
……
}
}
……
loadFields();
}
addAttributes()添加属性,通过ALERT TABLE语句完成,为表添加新的列。并在添加完成后调用私有函数loadFields()加载新的属性。
deleteAttribute()、renameAttributes()同样通过改表完成。
(15) changeAttributeValues() override、changeGeometryValues() override、changeFeatures() override
bool QgsPostgresProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
{}
bool QgsPostgresProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
{}
bool QgsPostgresProvider::changeFeatures( const QgsChangedAttributesMap &attr_map,
const QgsGeometryMap &geometry_map )
{}
changeAttributeValues()、changeGeometryValues()、changeFeatures()均通过UPDATE语句修改数据表。
(16) capabilities() override
QgsVectorDataProvider::Capabilities QgsPostgresProvider::capabilities() const
{
return mEnabledCapabilities;
}
capabilities()返回Provider的能力,由QgsVectorDataProvider::Capability枚举组合。
(17) hasSpatialIndex() override
QgsFeatureSource::SpatialIndexPresence QgsPostgresProvider::hasSpatialIndex() const
{
QgsPostgresProviderConnection conn( mUri.uri(), QVariantMap() );
try
{
return conn.spatialIndexExists( mUri.schema(), mUri.table(), mUri.geometryColumn() ) ? SpatialIndexPresent : SpatialIndexNotPresent;
}
catch ( QgsProviderConnectionException & )
{
return SpatialIndexUnknown;
}
}
bool QgsPostgresProviderConnection::spatialIndexExists( const QString &schema, const QString &name, const QString &geometryColumn ) const
{
checkCapability( Capability::SpatialIndexExists );
const QList<QVariantList> res = executeSql( QStringLiteral( R"""(SELECT COUNT(*)
FROM pg_class t, pg_class i, pg_namespace ns, pg_index ix, pg_attribute a
WHERE
t.oid=ix.indrelid
AND t.relnamespace=ns.oid
AND i.oid=ix.indexrelid
AND a.attrelid=t.oid
AND a.attnum=ANY(ix.indkey)
AND t.relkind IN ('r', 'm')
AND ns.nspname=%1
AND t.relname=%2
AND a.attname=%3;
)""" ).arg(
QgsPostgresConn::quotedValue( schema ),
QgsPostgresConn::quotedValue( name ),
QgsPostgresConn::quotedValue( geometryColumn ) ) );
return !res.isEmpty() && !res.at( 0 ).isEmpty() && res.at( 0 ).at( 0 ).toBool();
}
hasSpatialIndex()从数据库查询是否有空间索引。
(18) hasMetadata() override
bool QgsPostgresProvider::hasMetadata() const
{
bool hasMetadata = true;
QgsPostgresProvider::Relkind kind = relkind();
if ( kind == Relkind::View || kind == Relkind::MaterializedView )
{
hasMetadata = false;
}
return hasMetadata;
}
hasMetadata判断数据源是否有元数据,在relkind()函数内通过SQL语句(SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid)获取类型
(19) vectorLayerTypeFlags() override
Qgis::VectorLayerTypeFlags QgsPostgresProvider::vectorLayerTypeFlags() const
{
// 1)
Qgis::VectorLayerTypeFlags flags;
if ( mValid && mIsQuery )
{
flags.setFlag( Qgis::VectorLayerTypeFlag::SqlQuery );
}
return flags;
}
vectorLayerTypeFlags()返回矢量图层的类型标志,SqlQuery代表从Sql查询出矢量数据?
(20) crsToSrid()、sridToCrs()
static QMutex sMutex;
static QMap<int, QgsCoordinateReferenceSystem> sCrsCache;
int QgsPostgresProvider::crsToSrid( const QgsCoordinateReferenceSystem &crs, QgsPostgresConn *conn )
{
QMutexLocker locker( &sMutex );
……
}
QgsCoordinateReferenceSystem QgsPostgresProvider::sridToCrs( int srid, QgsPostgresConn *conn )
{
QMutexLocker locker( &sMutex );
……
}
crsToSrid()和sridToCrs()提供srid和QgsCoordinateReferenceSystem坐标对象的相互转换,在转换时首先从sCrsCache坐标缓存中查找,如果没有再访问数据库的spatial_ref_sys表获取数据,并将新数据存入缓存。两个函数均用一个互斥量加锁,确保了sCrsCache缓存的线程安全。
(21) setQuery()
void QgsPostgresProvider::setQuery( const QString &query )
{
mQuery = query;
mKind = Relkind::NotSet;
}
setQuery()设置查询的源,大多数情况下为表名,如:'sde'.'testTable',设置新源后,对应的relkind也应该重置。
(22) hasSufficientPermsAndCapabilities()
bool QgsPostgresProvider::hasSufficientPermsAndCapabilities()
{
……
if ( !mIsQuery )
{
// 1)
// Check that we can read from the table (i.e., we have select permission).
QString sql = QStringLiteral( "SELECT * FROM %1 LIMIT 1" ).arg( mQuery );
……
// 2)
if ( connectionRO()->pgVersion() >= 80400 )
{
sql = QString( "SELECT "
"has_table_privilege(%1,'DELETE'),"
"has_any_column_privilege(%1,'UPDATE'),"
"%2"
"has_table_privilege(%1,'INSERT'),"
"current_schema()" )
.arg( quotedValue( mQuery ),
mGeometryColumn.isNull()
? QStringLiteral( "'f'," )
: QStringLiteral( "has_column_privilege(%1,%2,'UPDATE')," )
.arg( quotedValue( mQuery ),
quotedValue( mGeometryColumn ) )
);
}
else
{
sql = QString( "SELECT "
"has_table_privilege(%1,'DELETE'),"
"has_table_privilege(%1,'UPDATE'),"
"has_table_privilege(%1,'UPDATE'),"
"has_table_privilege(%1,'INSERT'),"
"current_schema()" )
.arg( quotedValue( mQuery ) );
}
……
//3)
sql = QString( "SELECT 1 FROM pg_class,pg_namespace WHERE "
"pg_class.relnamespace=pg_namespace.oid AND "
"%3 AND "
"relname=%1 AND nspname=%2" )
.arg( quotedValue( mTableName ),
quotedValue( mSchemaName ),
connectionRO()->pgVersion() < 80100 ? "pg_get_userbyid(relowner)=current_user" : "pg_has_role(relowner,'MEMBER')" );
testAccess = connectionRO()->PQexec( sql );
if ( testAccess.PQresultStatus() == PGRES_TUPLES_OK && testAccess.PQntuples() == 1 )
{
mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes | QgsVectorDataProvider::RenameAttributes;
}
……
//4)
// supports layer metadata
mEnabledCapabilities |= QgsVectorDataProvider::ReadLayerMetadata;
}
hasSufficientPermsAndCapabilities()获取provider的权限和能力,并赋值给mEnabledCapabilities成员变量,由QgsVectorDataProvider::Capability枚举组合。
- 先尝试能否查询出数据,如果不能直接返回。
- 获取对表的CRUD权限。
- 判断属性的操作权限。
- 按需自行增加权限。
(23) loadFields()
bool QgsPostgresProvider::loadFields()
{
......
// 1)
// Get the table description
sql = QStringLiteral( "SELECT description FROM pg_description WHERE objoid=regclass(%1)::oid AND objsubid=0" ).arg( quotedValue( mQuery ) );
......
// 2)
// 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 );
......
if ( result.PQnfields() > 0 )
{
// 3)
// Collect attribiute oids
QSet<Oid> attroids;
for ( int i = 0; i < result.PQnfields(); i++ )
{
Oid attroid = result.PQftype( i );
attroids.insert( attroid );
}
// 4)
// Collect table oids
QSet<Oid> tableoids;
for ( int i = 0; i < result.PQnfields(); i++ )
{
Oid tableoid = result.PQftable( i );
if ( tableoid > 0 )
{
tableoids.insert( tableoid );
}
}
......
// 5)
// Collect formatted field types
sql = QStringLiteral(
"SELECT attrelid, attnum, pg_catalog.format_type(atttypid,atttypmod), pg_catalog.col_description(attrelid,attnum), pg_catalog.pg_get_expr(adbin,adrelid), atttypid, attnotnull::int, indisunique::int%1%2"
" FROM pg_attribute"
" LEFT OUTER JOIN pg_attrdef ON attrelid=adrelid AND attnum=adnum"
// find unique constraints if present. Text cast required to handle int2vector comparison. Distinct required as multiple unique constraints may exist
" LEFT OUTER JOIN ( SELECT DISTINCT indrelid, indkey, indisunique FROM pg_index WHERE indisunique ) uniq ON attrelid=indrelid AND attnum::text=indkey::text "
" WHERE attrelid IN %3"
).arg( connectionRO()->pgVersion() >= 100000 ? QStringLiteral( ", attidentity" ) : QString(),
connectionRO()->pgVersion() >= 120000 ? QStringLiteral( ", attgenerated" ) : QString(),
tableoidsFilter );
......
QString attGenerated = connectionRO()->pgVersion() >= 120000 ? fmtFieldTypeResult.PQgetvalue( i, 9 ) : "";
fmtFieldTypeMap[attrelid][attnum] = formatType;
descrMap[attrelid][attnum] = descr;
defValMap[attrelid][attnum] = defVal;
attTypeIdMap[attrelid][attnum] = attType;
notNullMap[attrelid][attnum] = attNotNull;
uniqueMap[attrelid][attnum] = uniqueConstraint;
identityMap[attrelid][attnum] = attIdentity.isEmpty() ? " " : attIdentity;
generatedMap[attrelid][attnum] = attGenerated.isEmpty() ? QString() : defVal;
......
// 6)
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 );
......
// 7)
for ( int i = 0; i < result.PQnfields(); i++ )
{
QString fieldName = result.PQfname( i );
if ( fieldName == mGeometryColumn )
continue;
......
}
}
}
loadFields()加载属性字段,该函数内用到了许多PG连接库的接口,接口含义可参考PGSQL编程,libpg官方文档
- 获取描述并保存在mDataComment变量。
- 从表中获取每个字段,LIMIT 0是因为只用获取字段属性。
- PQftype()获取该字段的类型oid并加入attroids。attroids和tableoids使用QSet,确保了数据唯一性。
- PQftable()获取字段所在表的oid并加入tableoids。
- 获取字段基本信息,并分别存入Map。
- attrelid:表oid
- attnum:字段在表中的列号
- formatType:类型,如:interger、character varying(50)、geometry
- descr:列(字段)表述
- defVal:字段默认值
- attType:字段类型oid
- attNotNull:是否为空
- uniqueConstraint:唯一性约束
- attIdentity:
- 将attroids的值拼成过滤条件,然后从pg_type表中查询出字段类型的具体数据。
- 遍历最开始查询的每个字段,为每个字段匹配数据(字段名、类型、类型名、长度、精度、注释),并创建QgsField对象,将对象追加到mAttributeFields中。
(24) convertField()
bool QgsPostgresProvider::convertField( QgsField &field, const QMap<QString, QVariant> *options )
{
switch ( field.type() )
{
}
field.setTypeName( fieldType );
}
convertField()将数据域进行适当转换,主要是判断QgsField的type(),因为type()返回的是Qt的数据类型QVariant::LongLong、QVariant::String等,然后设置typeName为容易识别的类型。
(25) searchLayers()
QList<QgsVectorLayer *> QgsPostgresProvider::searchLayers( const QList<QgsVectorLayer *> &layers, const QString &connectionInfo, const QString &schema, const QString &tableName )
{
QList<QgsVectorLayer *> result;
const auto constLayers = layers;
for ( QgsVectorLayer *layer : constLayers )
{
const QgsPostgresProvider *pgProvider = qobject_cast<QgsPostgresProvider *>( layer->dataProvider() );
if ( pgProvider &&
pgProvider->mUri.connectionInfo( false ) == connectionInfo && pgProvider->mSchemaName == schema && pgProvider->mTableName == tableName )
{
result.append( layer );
}
}
return result;
}
searchLayers()从给定的图层列表中搜寻图层,通过获取已给图层的provider,并从provider获取connectionInfo、schema和table判断与形参是否一致。
网友评论