Skip to content

Commit

Permalink
Merge pull request #34012 from elpaso/bugfix-gh33383-ogr-spatialite-r…
Browse files Browse the repository at this point in the history
…espect-provider-defaults

Dataprovider ogr spatialite respect provider defaults
  • Loading branch information
elpaso committed Jan 29, 2020
2 parents 3faeccf + be63036 commit ee323eb
Show file tree
Hide file tree
Showing 7 changed files with 356 additions and 54 deletions.
21 changes: 12 additions & 9 deletions src/core/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -1209,13 +1209,18 @@ void QgsOgrProvider::loadFields()
QString defaultValue = textEncoding()->toUnicode( OGR_Fld_GetDefault( fldDef ) );
if ( !defaultValue.isEmpty() && !OGR_Fld_IsDefaultDriverSpecific( fldDef ) )
{
if ( defaultValue.startsWith( '\'' ) )
{
defaultValue = defaultValue.remove( 0, 1 );
defaultValue.chop( 1 );
defaultValue.replace( QLatin1String( "''" ), QLatin1String( "'" ) );
}
mDefaultValues.insert( createdFields, defaultValue );
}

mAttributeFields.append( newField );
createdFields++;
}

}


Expand Down Expand Up @@ -1389,13 +1394,6 @@ QVariant QgsOgrProvider::defaultValue( int fieldId ) const
resultVar = QDate::currentDate();
else if ( defaultVal == QStringLiteral( "CURRENT_TIME" ) )
resultVar = QTime::currentTime();
else if ( defaultVal.startsWith( '\'' ) )
{
defaultVal = defaultVal.remove( 0, 1 );
defaultVal.chop( 1 );
defaultVal.replace( QLatin1String( "''" ), QLatin1String( "'" ) );
resultVar = defaultVal;
}

( void )mAttributeFields.at( fieldId ).convertCompatible( resultVar );
return resultVar;
Expand Down Expand Up @@ -1584,7 +1582,12 @@ bool QgsOgrProvider::addFeaturePrivate( QgsFeature &f, Flags flags )
OGRFieldType type = OGR_Fld_GetType( fldDef );

QVariant attrVal = attrs.at( qgisAttId );
if ( attrVal.isNull() || ( type != OFTString && attrVal.toString().isEmpty() ) )
// The field value is equal to the default (that might be a provider-side expression)
if ( mDefaultValues.contains( qgisAttId ) && attrVal.toString() == mDefaultValues.value( qgisAttId ) )
{
OGR_F_UnsetField( feature.get(), ogrAttId );
}
else if ( attrVal.isNull() || ( type != OFTString && attrVal.toString().isEmpty() ) )
{
// Starting with GDAL 2.2, there are 2 concepts: unset fields and null fields
// whereas previously there was only unset fields. For a GeoJSON output,
Expand Down
150 changes: 106 additions & 44 deletions src/providers/spatialite/qgsspatialiteprovider.cpp
Expand Up @@ -936,7 +936,7 @@ void QgsSpatiaLiteProvider::fetchConstraints()
if ( rows >= 1 )
{
QString tableSql = QString::fromUtf8( results[ 1 ] );
QRegularExpression rx( QStringLiteral( "[(,]\\s*(?:%1|\"%1\")\\s+INTEGER PRIMARY KEY AUTOINCREMENT" ).arg( mPrimaryKey ), QRegularExpression::CaseInsensitiveOption );
QRegularExpression rx( QStringLiteral( "[(,]\\s*(?:%1|\"%1\"|`%1`)\\s+INTEGER PRIMARY KEY AUTOINCREMENT" ).arg( mPrimaryKey ), QRegularExpression::CaseInsensitiveOption );
if ( tableSql.contains( rx ) )
{
mPrimaryKeyAutoIncrement = true;
Expand All @@ -956,19 +956,22 @@ void QgsSpatiaLiteProvider::insertDefaultValue( int fieldIndex, QString defaultV

if ( mAttributeFields.at( fieldIndex ).name() != mPrimaryKey || ( mAttributeFields.at( fieldIndex ).name() == mPrimaryKey && !mPrimaryKeyAutoIncrement ) )
{
bool ok;
switch ( mAttributeFields.at( fieldIndex ).type() )
{
case QVariant::LongLong:
defaultVariant = defaultVal.toLongLong();
defaultVariant = defaultVal.toLongLong( &ok );
break;

case QVariant::Double:
defaultVariant = defaultVal.toDouble();
defaultVariant = defaultVal.toDouble( &ok );
break;

default:
{
if ( defaultVal.startsWith( '\'' ) )
// Literal string?
ok = defaultVal.startsWith( '\'' );
if ( ok )
defaultVal = defaultVal.remove( 0, 1 );
if ( defaultVal.endsWith( '\'' ) )
defaultVal.chop( 1 );
Expand All @@ -978,11 +981,56 @@ void QgsSpatiaLiteProvider::insertDefaultValue( int fieldIndex, QString defaultV
break;
}
}

if ( ! ok ) // Must be a SQL clause and not a literal
{
mDefaultValueClause.insert( fieldIndex, defaultVal );
}

}
mDefaultValues.insert( fieldIndex, defaultVariant );
mDefaultValues.insert( fieldIndex, defaultVal );
}
}


QVariant QgsSpatiaLiteProvider::defaultValue( int fieldId ) const
{
// TODO: backend-side evaluation
if ( fieldId < 0 || fieldId >= mAttributeFields.count() )
return QVariant();

QString defaultVal = mDefaultValues.value( fieldId, QString() );
if ( defaultVal.isEmpty() )
return QVariant();

QVariant resultVar = defaultVal;
if ( defaultVal == QStringLiteral( "CURRENT_TIMESTAMP" ) )
resultVar = QDateTime::currentDateTime();
else if ( defaultVal == QStringLiteral( "CURRENT_DATE" ) )
resultVar = QDate::currentDate();
else if ( defaultVal == QStringLiteral( "CURRENT_TIME" ) )
resultVar = QTime::currentTime();
else if ( defaultVal.startsWith( '\'' ) )
{
defaultVal = defaultVal.remove( 0, 1 );
defaultVal.chop( 1 );
defaultVal.replace( QLatin1String( "''" ), QLatin1String( "'" ) );
resultVar = defaultVal;
}

( void )mAttributeFields.at( fieldId ).convertCompatible( resultVar );
return resultVar;
}

QString QgsSpatiaLiteProvider::defaultValueClause( int fieldIndex ) const
{
if ( mAttributeFields.at( fieldIndex ).name() == mPrimaryKey && mPrimaryKeyAutoIncrement )
{
return tr( "Autogenerate" );
}
return mDefaultValueClause.value( fieldIndex, QString() );
}

void QgsSpatiaLiteProvider::handleError( const QString &sql, char *errorMessage, bool rollback )
{
QgsMessageLog::logMessage( tr( "SQLite error: %2\nSQL: %1" ).arg( sql, errorMessage ? errorMessage : tr( "unknown cause" ) ), tr( "SpatiaLite" ) );
Expand Down Expand Up @@ -3957,10 +4005,11 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
sqlite3_stmt *stmt = nullptr;
char *errMsg = nullptr;
bool toCommit = false;
QString sql;
QString values;
QString baseValues;
QString separator;
int ia, ret;
// SQL for single row
QString sql;

if ( flist.isEmpty() )
return true;
Expand All @@ -3971,46 +4020,58 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
{
toCommit = true;

sql = QStringLiteral( "INSERT INTO %1(" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) );
values = QStringLiteral( ") VALUES (" );
QString baseSql { QStringLiteral( "INSERT INTO %1(" ).arg( QgsSqliteUtils::quotedIdentifier( mTableName ) ) };
baseValues = QStringLiteral( ") VALUES (" );
separator.clear();

if ( !mGeometryColumn.isEmpty() )
{
sql += separator + QgsSqliteUtils::quotedIdentifier( mGeometryColumn );
values += separator + geomParam();
baseSql += separator + QgsSqliteUtils::quotedIdentifier( mGeometryColumn );
baseValues += separator + geomParam();
separator = ',';
}

for ( int i = 0; i < attributevec.count(); ++i )
for ( QgsFeatureList::iterator feature = flist.begin(); feature != flist.end(); ++feature )
{
if ( i >= mAttributeFields.count() )
continue;

QString fieldname = mAttributeFields.at( i ).name();
if ( fieldname.isEmpty() || fieldname == mGeometryColumn )
continue;
QString values { baseValues };
sql = baseSql;

sql += separator + QgsSqliteUtils::quotedIdentifier( fieldname );
values += separator + '?';
separator = ',';
}
// looping on each feature to insert
QgsAttributes attributevec = feature->attributes();

sql += values;
sql += ')';
// Default indexes (to be skipped)
QList<int> defaultIndexes;

// SQLite prepared statement
ret = sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr );
if ( ret == SQLITE_OK )
{
for ( QgsFeatureList::iterator feature = flist.begin(); feature != flist.end(); ++feature )
for ( int i = 0; i < attributevec.count(); ++i )
{
// looping on each feature to insert
QgsAttributes attributevec = feature->attributes();
if ( mDefaultValues.contains( i ) && mDefaultValues.value( i ) == attributevec.at( i ).toString() )
{
defaultIndexes.push_back( i );
continue;
}

if ( i >= mAttributeFields.count() )
continue;

QString fieldname = mAttributeFields.at( i ).name();
if ( fieldname.isEmpty() || fieldname == mGeometryColumn )
{
continue;
}

sql += separator + QgsSqliteUtils::quotedIdentifier( fieldname );
values += separator + '?';
separator = ',';
}

// resetting Prepared Statement and bindings
sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt );
sql += values;
sql += ')';

// SQLite prepared statement
ret = sqlite3_prepare_v2( mSqliteHandle, sql.toUtf8().constData(), -1, &stmt, nullptr );
if ( ret == SQLITE_OK )
{

// initializing the column counter
ia = 0;
Expand Down Expand Up @@ -4039,6 +4100,11 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )

for ( int i = 0; i < attributevec.count(); ++i )
{
if ( defaultIndexes.contains( i ) )
{
continue;
}

QVariant v = attributevec.at( i );

// binding values for each attribute
Expand Down Expand Up @@ -4103,6 +4169,8 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
// performing actual row insert
ret = sqlite3_step( stmt );

sqlite3_finalize( stmt );

if ( ret == SQLITE_DONE || ret == SQLITE_ROW )
{
// update feature id
Expand All @@ -4121,14 +4189,13 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList &flist, Flags flags )
break;
}
}
} // prepared statement

sqlite3_finalize( stmt );
if ( ret == SQLITE_DONE || ret == SQLITE_ROW )
{
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
}

if ( ret == SQLITE_DONE || ret == SQLITE_ROW )
{
ret = sqlite3_exec( mSqliteHandle, "COMMIT", nullptr, nullptr, &errMsg );
}
} // prepared statement
} // BEGIN statement

if ( ret != SQLITE_OK )
Expand Down Expand Up @@ -4526,11 +4593,6 @@ QgsVectorDataProvider::Capabilities QgsSpatiaLiteProvider::capabilities() const
return mEnabledCapabilities;
}

QVariant QgsSpatiaLiteProvider::defaultValue( int fieldId ) const
{
return mDefaultValues.value( fieldId, QVariant() );
}

bool QgsSpatiaLiteProvider::skipConstraintCheck( int fieldIndex, QgsFieldConstraints::Constraint constraint, const QVariant &value ) const
{
Q_UNUSED( constraint )
Expand Down
8 changes: 7 additions & 1 deletion src/providers/spatialite/qgsspatialiteprovider.h
Expand Up @@ -221,6 +221,9 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider

QgsFields mAttributeFields;

//! Map of field index to default value SQL fragments
QMap<int, QString> mDefaultValueClause;

//! Flag indicating if the layer data source is a valid SpatiaLite layer
bool mValid = false;

Expand Down Expand Up @@ -264,7 +267,7 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider
QString mGeometryColumn;

//! Map of field index to default value
QMap<int, QVariant> mDefaultValues;
QMap<int, QString> mDefaultValues;

//! Name of the SpatialIndex table
QString mIndexTable;
Expand Down Expand Up @@ -382,6 +385,9 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider

friend class QgsSpatiaLiteFeatureSource;

// QgsVectorDataProvider interface
public:
virtual QString defaultValueClause( int fieldIndex ) const override;
};

class QgsSpatiaLiteProviderMetadata: public QgsProviderMetadata
Expand Down

0 comments on commit ee323eb

Please sign in to comment.