QgsPostgresProvider源码分析(2)之QgsPostgresProvider

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;
  }
……
}

构造函数

  1. 首先从uri里提取出表名、几何列等基本信息放到成员变量里。
  2. 然后提取schema,表名等,通过setQuery()设置查询源,如:schema为sde,表名为testTable,则查询源为"sde"."testTable",该值由成员变量mQuery保存,后续的sql语句将直接使用该变量值。
  3. 通过hasSufficientPermsAndCapabilities()判断数据库增删改查等权限并将权限赋值给mEnabledCapabilities。
  4. 从数据库查询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()返回要素数量。

  1. 从mShared中获取要素数量,mShared为std::shared_ptr<QgsPostgresSharedData> 类型。
  2. 如果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()添加要素。

  1. 从flist中判断每个要素的主键属性,如果有值且非默认值,则认为有主键值,skipSinglePKField置false,否则置true。skipSinglePKField:当只有单主键时,如果主键是序列,那么插入要素时可以考虑忽略主键。
  2. 遍历第一个要素的所有属性,组装完Insert语句的字段。
  3. 遍历要素,获取每个要素的属性值,组装Values。
  4. 执行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枚举组合。

  1. 先尝试能否查询出数据,如果不能直接返回。
  2. 获取对表的CRUD权限。
  3. 判断属性的操作权限。
  4. 按需自行增加权限。

(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官方文档

  1. 获取描述并保存在mDataComment变量。
  2. 从表中获取每个字段,LIMIT 0是因为只用获取字段属性。
  3. PQftype()获取该字段的类型oid并加入attroids。attroids和tableoids使用QSet,确保了数据唯一性。
  4. PQftable()获取字段所在表的oid并加入tableoids。
  5. 获取字段基本信息,并分别存入Map。
  • attrelid:表oid
  • attnum:字段在表中的列号
  • formatType:类型,如:interger、character varying(50)、geometry
  • descr:列(字段)表述
  • defVal:字段默认值
  • attType:字段类型oid
  • attNotNull:是否为空
  • uniqueConstraint:唯一性约束
  • attIdentity:
  1. 将attroids的值拼成过滤条件,然后从pg_type表中查询出字段类型的具体数据。
  2. 遍历最开始查询的每个字段,为每个字段匹配数据(字段名、类型、类型名、长度、精度、注释),并创建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判断与形参是否一致。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容