Skip to content

Commit

Permalink
Extended enum support in postgres provider to work also with domain c…
Browse files Browse the repository at this point in the history
…heck constrains (at the moment only for constrain types like VALUE in ('a', 'b', 'c'))

git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@10940 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
mhugent committed Jun 17, 2009
1 parent 380173a commit 8a5581a
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 26 deletions.
5 changes: 5 additions & 0 deletions src/app/qgsattributedialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat
{
cb->addItem(*s_it);
}
int idx = cb->findText( myFieldValue.toString() );
if ( idx >= 0 )
{
cb->setCurrentIndex( idx );
}
myWidget = cb;
}
break;
Expand Down
120 changes: 94 additions & 26 deletions src/providers/postgres/qgspostgresprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1696,48 +1696,116 @@ void QgsPostgresProvider::enumValues( int index, QStringList& enumList )
return;
}

//is type an enum or a domain type?
//is type an enum?
QString typeSql = QString("SELECT typtype FROM pg_type where typname = %1").arg(quotedValue(typeName));
Result typeRes = connectionRO->PQexec( typeSql );
if ( PQresultStatus( typeRes ) != PGRES_TUPLES_OK || PQntuples(typeRes) < 1)
{
return;
}


QString typtype = PQgetvalue( typeRes, 0, 0 );
if(typtype.compare("e", Qt::CaseInsensitive) == 0)
{
//parse enum_range
QString enumRangeSql = QString("SELECT enum_range(%1) from %2 limit1").arg(quotedIdentifier(f_it.value().name())).arg(mSchemaTableName);
Result enumRangeRes = connectionRO->PQexec(enumRangeSql);
if ( PQresultStatus( enumRangeRes ) != PGRES_TUPLES_OK || PQntuples(enumRangeRes) > 0)
{
QString enumRangeString = PQgetvalue(enumRangeRes, 0, 0);
//strip away the brackets at begin and end
enumRangeString.chop(1);
enumRangeString.remove(0, 1);
QStringList rangeSplit = enumRangeString.split(",");
QStringList::const_iterator range_it = rangeSplit.constBegin();
for(; range_it != rangeSplit.constEnd(); ++range_it)
//try to read enum_range of attribute
if(!parseEnumRange(enumList, f_it->name()))
{
enumList.clear();
}
}
else
{
//is there a domain check constraint for the attribute?
if(!parseDomainCheckConstraint(enumList, f_it->name()))
{
enumList.clear();
}
}
}

bool QgsPostgresProvider::parseEnumRange(QStringList& enumValues, const QString& attributeName) const
{
enumValues.clear();
QString enumRangeSql = QString("SELECT enum_range(%1) from %2 limit1").arg(quotedIdentifier(attributeName)).arg(mSchemaTableName);
Result enumRangeRes = connectionRO->PQexec(enumRangeSql);
if ( PQresultStatus( enumRangeRes ) == PGRES_TUPLES_OK && PQntuples(enumRangeRes) > 0)
{
QString enumRangeString = PQgetvalue(enumRangeRes, 0, 0);
//strip away the brackets at begin and end
enumRangeString.chop(1);
enumRangeString.remove(0, 1);
QStringList rangeSplit = enumRangeString.split(",");
QStringList::const_iterator range_it = rangeSplit.constBegin();
for(; range_it != rangeSplit.constEnd(); ++range_it)
{
QString currentEnumValue = *range_it;
//remove quotes from begin and end of the value
if(currentEnumValue.startsWith("'") || currentEnumValue.startsWith("\""))
{
QString currentEnumValue = *range_it;
//remove quotes from begin and end of the value
if(currentEnumValue.startsWith("'") || currentEnumValue.startsWith("\""))
{
currentEnumValue.remove(0, 1);
}
if(currentEnumValue.endsWith("'") || currentEnumValue.endsWith("\""))
{
currentEnumValue.chop(1);
}
enumList << currentEnumValue;
currentEnumValue.remove(0, 1);
}
if(currentEnumValue.endsWith("'") || currentEnumValue.endsWith("\""))
{
currentEnumValue.chop(1);
}
enumValues << currentEnumValue;
}
return true;
}
else if (typtype.compare("d", Qt::CaseInsensitive) == 0)
return false;
}

bool QgsPostgresProvider::parseDomainCheckConstraint(QStringList& enumValues, const QString& attributeName) const
{
enumValues.clear();

//is it a domain type with a check constraint?
QString domainSql = QString("SELECT domain_name from information_schema.columns where table_name = %1 and column_name = %2").arg(quotedValue(mTableName)).arg(quotedValue(attributeName));
Result domainResult = connectionRO->PQexec(domainSql);
if ( PQresultStatus( domainResult ) == PGRES_TUPLES_OK && PQntuples(domainResult) > 0)
{
//a domain type. Todo: evaluate the check constraint
//a domain type
QString domainCheckDefinitionSql = QString("SELECT consrc FROM pg_constraint where conname = (SELECT constraint_name FROM information_schema.domain_constraints WHERE domain_name = %1)").arg(quotedValue(PQgetvalue(domainResult, 0, 0)));
Result domainCheckRes = connectionRO->PQexec(domainCheckDefinitionSql);
if ( PQresultStatus(domainCheckRes) == PGRES_TUPLES_OK && PQntuples(domainCheckRes) > 0)
{
QString checkDefinition = PQgetvalue(domainCheckRes, 0, 0);

//we assume that the constraint is of the following form:
//(VALUE = ANY (ARRAY['a'::text, 'b'::text, 'c'::text, 'd'::text]))
//normally, postgresql creates that if the contstraint has been specified as 'VALUE in ('a', 'b', 'c', 'd')

//todo: ANY must occure before ARRAY
int anyPos = checkDefinition.indexOf("VALUE = ANY");
int arrayPosition = checkDefinition.lastIndexOf("ARRAY[");
int closingBracketPos = checkDefinition.indexOf("]", arrayPosition + 6);

if(anyPos == -1 || anyPos >= arrayPosition)
{
return false; //constraint has not the required format
}

if(arrayPosition != -1)
{
QString valueList = checkDefinition.mid(arrayPosition + 6, closingBracketPos);
QStringList commaSeparation = valueList.split(",", QString::SkipEmptyParts);
QStringList::const_iterator cIt = commaSeparation.constBegin();
for(; cIt != commaSeparation.constEnd(); ++cIt)
{
//get string between ''
int beginQuotePos = cIt->indexOf("'");
int endQuotePos = cIt->lastIndexOf("'");
if(beginQuotePos != -1 && (endQuotePos - beginQuotePos) > 1)
{
enumValues << cIt->mid(beginQuotePos + 1, endQuotePos - beginQuotePos - 1);
}
}
}
return true;
}
}
return false;
}

// Returns the maximum value of an attribute
Expand Down
13 changes: 13 additions & 0 deletions src/providers/postgres/qgspostgresprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,19 @@ class QgsPostgresProvider : public QgsVectorDataProvider
*/
void loadFields();

/**Parses the enum_range of an attribute and inserts the possible values into a stringlist
@param enumValues the stringlist where the values are appended
@param attributeName the name of the enum attribute
@return true in case of success and fals in case of error (e.g. if the type is not an enum type)*/
bool parseEnumRange(QStringList& enumValues, const QString& attributeName) const;

/** Parses the possible enum values of a domain type (given in the check constraint of the domain type)
@param enumValues Reference to list that receives enum values
@param attributeName Name of the domain type attribute
@return true in case of success and false in case of error (e.g. if the attribute is not a domain type or does not have a check constraint)
*/
bool parseDomainCheckConstraint(QStringList& enumValues, const QString& attributeName) const;

bool mFetching; // true if a cursor was declared
std::vector < QgsFeature > features;
QgsFieldMap attributeFields;
Expand Down

0 comments on commit 8a5581a

Please sign in to comment.