mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-28 00:17:30 -05:00
[FEATURE] postgres provider: add support for generated identity columns (implements #21745)
This commit is contained in:
parent
8b3f97fa19
commit
59dd85103d
@ -758,7 +758,7 @@ bool QgsPostgresProvider::loadFields()
|
||||
}
|
||||
|
||||
|
||||
QMap<int, QMap<int, QString> > fmtFieldTypeMap, descrMap, defValMap;
|
||||
QMap<int, QMap<int, QString> > fmtFieldTypeMap, descrMap, defValMap, identityMap;
|
||||
QMap<int, QMap<int, int> > attTypeIdMap;
|
||||
QMap<int, QMap<int, bool> > notNullMap, uniqueMap;
|
||||
if ( result.PQnfields() > 0 )
|
||||
@ -785,14 +785,17 @@ bool QgsPostgresProvider::loadFields()
|
||||
QString tableoidsFilter = '(' + tableoidsList.join( QStringLiteral( "," ) ) + ')';
|
||||
|
||||
// Collect formatted field types
|
||||
sql = "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"
|
||||
" FROM pg_attribute"
|
||||
" LEFT OUTER JOIN pg_attrdef ON attrelid=adrelid AND attnum=adnum"
|
||||
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"
|
||||
" 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 "
|
||||
// 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 %2"
|
||||
).arg( connectionRO()->pgVersion() >= 100000 ? QStringLiteral( ", attidentity" ) : QString() ).arg( tableoidsFilter );
|
||||
|
||||
" WHERE attrelid IN " + tableoidsFilter;
|
||||
QgsPostgresResult fmtFieldTypeResult( connectionRO()->PQexec( sql ) );
|
||||
for ( int i = 0; i < fmtFieldTypeResult.PQntuples(); ++i )
|
||||
{
|
||||
@ -804,18 +807,21 @@ bool QgsPostgresProvider::loadFields()
|
||||
int attType = fmtFieldTypeResult.PQgetvalue( i, 5 ).toInt();
|
||||
bool attNotNull = fmtFieldTypeResult.PQgetvalue( i, 6 ).toInt();
|
||||
bool uniqueConstraint = fmtFieldTypeResult.PQgetvalue( i, 7 ).toInt();
|
||||
QString attIdentity = connectionRO()->pgVersion() >= 100000 ? fmtFieldTypeResult.PQgetvalue( i, 8 ) : " ";
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QSet<QString> fields;
|
||||
mAttributeFields.clear();
|
||||
mIdentityFields.clear();
|
||||
for ( int i = 0; i < result.PQnfields(); i++ )
|
||||
{
|
||||
QString fieldName = result.PQfname( i );
|
||||
@ -1054,6 +1060,7 @@ bool QgsPostgresProvider::loadFields()
|
||||
constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginProvider );
|
||||
newField.setConstraints( constraints );
|
||||
|
||||
mIdentityFields.insert( mAttributeFields.size(), identityMap[tableoid][attnum][0].toLatin1() );
|
||||
mAttributeFields.append( newField );
|
||||
}
|
||||
|
||||
@ -1329,19 +1336,40 @@ bool QgsPostgresProvider::determinePrimaryKey()
|
||||
QgsDebugMsg( QStringLiteral( "Relation is a table. Checking to see if it has an oid column." ) );
|
||||
|
||||
mPrimaryKeyAttrs.clear();
|
||||
mPrimaryKeyType = PktUnknown;
|
||||
|
||||
// If there is an oid on the table, use that instead,
|
||||
sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attname='oid' AND attrelid=regclass(%1)" ).arg( quotedValue( mQuery ) );
|
||||
|
||||
res = connectionRO()->PQexec( sql );
|
||||
if ( res.PQntuples() == 1 )
|
||||
if ( connectionRO()->pgVersion() >= 100000 )
|
||||
{
|
||||
// Could warn the user here that performance will suffer if
|
||||
// oid isn't indexed (and that they may want to add a
|
||||
// primary key to the table)
|
||||
mPrimaryKeyType = PktOid;
|
||||
// If there is an generated id on the table, use that instead,
|
||||
sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attidentity IN ('a','d') AND attrelid=regclass(%1) LIMIT 1" ).arg( quotedValue( mQuery ) );
|
||||
res = connectionRO()->PQexec( sql );
|
||||
if ( res.PQntuples() == 1 )
|
||||
{
|
||||
// Could warn the user here that performance will suffer if
|
||||
// attribute isn't indexed (and that they may want to add a
|
||||
// primary key to the table)
|
||||
int idx = fieldNameIndex( res.PQgetvalue( 0, 0 ) );
|
||||
mPrimaryKeyType = pkType( mAttributeFields.at( idx ) );
|
||||
mPrimaryKeyAttrs << idx;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if ( mPrimaryKeyType == PktUnknown )
|
||||
{
|
||||
// If there is an oid on the table, use that instead,
|
||||
sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attname='oid' AND attrelid=regclass(%1)" ).arg( quotedValue( mQuery ) );
|
||||
|
||||
res = connectionRO()->PQexec( sql );
|
||||
if ( res.PQntuples() == 1 )
|
||||
{
|
||||
// Could warn the user here that performance will suffer if
|
||||
// oid isn't indexed (and that they may want to add a
|
||||
// primary key to the table)
|
||||
mPrimaryKeyType = PktOid;
|
||||
}
|
||||
}
|
||||
|
||||
if ( mPrimaryKeyType == PktUnknown )
|
||||
{
|
||||
sql = QStringLiteral( "SELECT attname FROM pg_attribute WHERE attname='ctid' AND attrelid=regclass(%1)" ).arg( quotedValue( mQuery ) );
|
||||
|
||||
@ -1353,10 +1381,11 @@ bool QgsPostgresProvider::determinePrimaryKey()
|
||||
QgsMessageLog::logMessage( tr( "Primary key is ctid - changing of existing features disabled (%1; %2)" ).arg( mGeometryColumn, mQuery ) );
|
||||
mEnabledCapabilities &= ~( QgsVectorDataProvider::DeleteFeatures | QgsVectorDataProvider::ChangeAttributeValues | QgsVectorDataProvider::ChangeGeometries | QgsVectorDataProvider::ChangeFeatures );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "The table has no column suitable for use as a key. QGIS requires a primary key, a PostgreSQL oid column or a ctid for tables." ), tr( "PostGIS" ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( mPrimaryKeyType == PktUnknown )
|
||||
{
|
||||
QgsMessageLog::logMessage( tr( "The table has no column suitable for use as a key. QGIS requires a primary key, a PostgreSQL oid column or a ctid for tables." ), tr( "PostGIS" ) );
|
||||
}
|
||||
}
|
||||
else if ( type == Relkind::View || type == Relkind::MaterializedView )
|
||||
@ -2026,7 +2055,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags )
|
||||
|
||||
// Prepare the INSERT statement
|
||||
QString insert = QStringLiteral( "INSERT INTO %1(" ).arg( mQuery );
|
||||
QString values = QStringLiteral( ") VALUES (" );
|
||||
QString values;
|
||||
QString delim;
|
||||
int offset = 1;
|
||||
|
||||
@ -2046,6 +2075,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags )
|
||||
// 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;
|
||||
|
||||
if ( ( mPrimaryKeyType == PktInt || mPrimaryKeyType == PktFidMap || mPrimaryKeyType == PktUint64 ) )
|
||||
{
|
||||
@ -2074,6 +2104,8 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags )
|
||||
{
|
||||
for ( int idx : mPrimaryKeyAttrs )
|
||||
{
|
||||
if ( mIdentityFields[idx] == 'a' )
|
||||
overrideIdentity = true;
|
||||
insert += delim + quotedIdentifier( field( idx ).name() );
|
||||
values += delim + QStringLiteral( "$%1" ).arg( defaultValues.size() + offset );
|
||||
delim = ',';
|
||||
@ -2118,6 +2150,9 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags )
|
||||
|
||||
insert += delim + quotedIdentifier( fieldname );
|
||||
|
||||
if ( mIdentityFields[idx] == 'a' )
|
||||
overrideIdentity = true;
|
||||
|
||||
QString defVal = defaultValueClause( idx );
|
||||
|
||||
if ( i == flist.size() )
|
||||
@ -2182,7 +2217,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, Flags flags )
|
||||
delim = ',';
|
||||
}
|
||||
|
||||
insert += values + ')';
|
||||
insert += QStringLiteral( ") %1VALUES (%2)" ).arg( overrideIdentity ? "OVERRIDING SYSTEM VALUE " : "" ).arg( values );
|
||||
|
||||
if ( !( flags & QgsFeatureSink::FastInsert ) )
|
||||
{
|
||||
|
@ -323,6 +323,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider
|
||||
QgsAttrPalIndexNameHash mAttrPalIndexName;
|
||||
|
||||
QgsFields mAttributeFields;
|
||||
QHash<int, char> mIdentityFields;
|
||||
QString mDataComment;
|
||||
|
||||
//! Data source URI struct for this layer
|
||||
|
Loading…
x
Reference in New Issue
Block a user