Skip to content

Commit

Permalink
[FEATURE] Binary blob support for OGR provider
Browse files Browse the repository at this point in the history
Instead of converting binary fields to truncated strings, we instead
store their contents as QByteArray values, allowing the original binary
content to be retrieved.

This allows for plugins and scripts to utilise binary fields,
such as extracting their contents.
  • Loading branch information
nyalldawson committed Nov 12, 2018
1 parent 8ae1880 commit 5c27b7d
Show file tree
Hide file tree
Showing 56 changed files with 59 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/core/qgsfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ QString QgsField::displayString( const QVariant &v ) const
QJsonDocument doc = QJsonDocument::fromVariant( v );
return QString::fromUtf8( doc.toJson().data() );
}
else if ( d->type == QVariant::ByteArray )
{
return QObject::tr( "BLOB" );
}
// Fallback if special rules do not apply
return v.toString();
}
Expand Down
11 changes: 11 additions & 0 deletions src/core/qgsogrutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,17 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
value = QDateTime( QDate( year, month, day ), QTime( hour, minute, second ) );
}
break;

case QVariant::ByteArray:
{
int size = 0;
const GByte *b = OGR_F_GetFieldAsBinary( ogrFet, attIndex, &size );
QByteArray ba = QByteArray::fromRawData( reinterpret_cast<const char *>( b ), size );
ba.detach();
value = ba;
break;
}

default:
Q_ASSERT_X( false, "QgsOgrUtils::getOgrFeatureAttribute", "unsupported field type" );
if ( ok )
Expand Down
20 changes: 20 additions & 0 deletions src/providers/ogr/qgsogrprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,11 @@ void QgsOgrProvider::loadFields()
case OFTDateTime:
varType = QVariant::DateTime;
break;

case OFTBinary:
varType = QVariant::ByteArray;
break;

case OFTString:
default:
varType = QVariant::String; // other unsupported, leave it as a string
Expand Down Expand Up @@ -1504,6 +1509,13 @@ bool QgsOgrProvider::addFeaturePrivate( QgsFeature &f, Flags flags )
OGR_F_SetFieldString( feature.get(), ogrAttId, textEncoding()->fromUnicode( attrVal.toString() ).constData() );
break;

case OFTBinary:
{
const QByteArray ba = attrVal.toByteArray();
OGR_F_SetFieldBinary( feature.get(), ogrAttId, ba.size(), const_cast< GByte * >( reinterpret_cast< const GByte * >( ba.data() ) ) );
break;
}

default:
QgsMessageLog::logMessage( tr( "type %1 for attribute %2 not found" ).arg( type ).arg( qgisAttId ), tr( "OGR" ) );
break;
Expand Down Expand Up @@ -2059,6 +2071,14 @@ bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_
case OFTString:
OGR_F_SetFieldString( of.get(), f, textEncoding()->fromUnicode( it2->toString() ).constData() );
break;

case OFTBinary:
{
const QByteArray ba = it2->toByteArray();
OGR_F_SetFieldBinary( of.get(), f, ba.size(), const_cast< GByte * >( reinterpret_cast< const GByte * >( ba.data() ) ) );
break;
}

default:
pushError( tr( "Type %1 of attribute %2 of feature %3 unknown." ).arg( type ).arg( fid ).arg( f ) );
break;
Expand Down
5 changes: 5 additions & 0 deletions tests/src/core/testqgsfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,11 @@ void TestQgsField::displayString()
QCOMPARE( longField.displayString( 5 ), QString( "5" ) );
QCOMPARE( longField.displayString( 599999898999LL ), QString( "599999898999" ) );

// binary field
QgsField binaryField( QStringLiteral( "binary" ), QVariant::ByteArray, QStringLiteral( "Binary" ) );
QString testBAString( QStringLiteral( "test string" ) );
QByteArray testBA( testBAString.toLocal8Bit() );
QCOMPARE( binaryField.displayString( testBA ), QStringLiteral( "BLOB" ) );
}

void TestQgsField::convertCompatible()
Expand Down
20 changes: 19 additions & 1 deletion tests/src/python/test_provider_ogr.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
import shutil
import sys
import tempfile
import hashlib

from osgeo import gdal, ogr # NOQA
from qgis.PyQt.QtCore import QVariant
from qgis.PyQt.QtCore import QVariant, QByteArray
from qgis.core import (QgsApplication,
QgsRectangle,
QgsProviderRegistry,
Expand Down Expand Up @@ -489,6 +490,23 @@ def testOSM(self):
self.assertEqual(f.id(), 8)
del it

def testBinaryField(self):
source = os.path.join(TEST_DATA_DIR, 'attachments.gdb')
vl = QgsVectorLayer(source + "|layername=points__ATTACH")
self.assertTrue(vl.isValid())

fields = vl.fields()
data_field = fields[fields.lookupField('DATA')]
self.assertEqual(data_field.type(), QVariant.ByteArray)
self.assertEqual(data_field.typeName(), 'Binary')

features = {f['ATTACHMENTID']: f for f in vl.getFeatures()}
self.assertEqual(len(features), 2)
self.assertIsInstance(features[1]['DATA'], QByteArray)
self.assertEqual(hashlib.md5(features[1]['DATA'].data()).hexdigest(), 'ef3dbc530cc39a545832a6c82aac57b6')
self.assertIsInstance(features[2]['DATA'], QByteArray)
self.assertEqual(hashlib.md5(features[2]['DATA'].data()).hexdigest(), '4b952b80e4288ca5111be2f6dd5d6809')


if __name__ == '__main__':
unittest.main()
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000001.freelist
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000001.gdbindexes
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000001.gdbtable
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000001.gdbtablx
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000002.gdbtable
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000002.gdbtablx
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000003.gdbindexes
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000003.gdbtable
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000003.gdbtablx
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000004.freelist
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000004.gdbindexes
Binary file not shown.
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000004.gdbtablx
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000004.spx
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000005.gdbindexes
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000005.gdbtable
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000005.gdbtablx
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000006.freelist
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000006.gdbindexes
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000006.gdbtable
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000006.gdbtablx
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000007.gdbindexes
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000007.gdbtable
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000007.gdbtablx
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000016.gdbindexes
Binary file not shown.
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000016.gdbtablx
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000016.spx
Binary file not shown.
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000017.gdbindexes
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000017.gdbtable
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/a00000017.gdbtablx
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/gdb
Binary file not shown.
Binary file added tests/testdata/attachments.gdb/timestamps
Binary file not shown.

0 comments on commit 5c27b7d

Please sign in to comment.