Skip to content

Commit

Permalink
[VirtualLayer] Prevent integer overflow for long long in virtual layer (
Browse files Browse the repository at this point in the history
#41332)

* Fixes #40503 : Prevent integer overflow for long long in virtual layer
  • Loading branch information
troopa81 committed Feb 5, 2021
1 parent ec6552c commit 67bf72c
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 10 deletions.
7 changes: 5 additions & 2 deletions src/core/qgsvirtuallayerdefinition.cpp
Expand Up @@ -146,7 +146,7 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinition::fromUrl( const QUrl &url )
QString fieldType( reField.cap( 2 ) );
if ( fieldType == QLatin1String( "int" ) )
{
fields.append( QgsField( fieldName, QVariant::Int, fieldType ) );
fields.append( QgsField( fieldName, QVariant::LongLong, fieldType ) );
}
else if ( fieldType == QLatin1String( "real" ) )
{
Expand Down Expand Up @@ -293,7 +293,10 @@ QUrl QgsVirtualLayerDefinition::toUrl() const
const auto constFields = fields();
for ( const QgsField &f : constFields )
{
if ( f.type() == QVariant::Int )
if ( f.type() == QVariant::Int
|| f.type() == QVariant::UInt
|| f.type() == QVariant::Bool
|| f.type() == QVariant::LongLong )
urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":int" );
else if ( f.type() == QVariant::Double )
urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":real" );
Expand Down
6 changes: 3 additions & 3 deletions src/providers/virtual/qgsvirtuallayerqueryparser.cpp
Expand Up @@ -82,7 +82,7 @@ namespace QgsVirtualLayerQueryParser
ColumnDef def;
def.setName( column );
if ( type == QLatin1String( "int" ) )
def.setScalarType( QVariant::Int );
def.setScalarType( QVariant::LongLong );
else if ( type == QLatin1String( "real" ) )
def.setScalarType( QVariant::Double );
else if ( type == QLatin1String( "text" ) )
Expand Down Expand Up @@ -111,7 +111,7 @@ namespace QgsVirtualLayerQueryParser
// the type declared by one of the virtual tables
// or null
if ( columnType == QLatin1String( "int" ) )
d.setScalarType( QVariant::Int );
d.setScalarType( QVariant::LongLong );
else if ( columnType == QLatin1String( "real" ) )
d.setScalarType( QVariant::Double );
else if ( columnType == QLatin1String( "text" ) )
Expand Down Expand Up @@ -223,7 +223,7 @@ namespace QgsVirtualLayerQueryParser
switch ( type )
{
case SQLITE_INTEGER:
tableDef[colIdx].setScalarType( QVariant::Int );
tableDef[colIdx].setScalarType( QVariant::LongLong );
break;
case SQLITE_FLOAT:
tableDef[colIdx].setScalarType( QVariant::Double );
Expand Down
4 changes: 3 additions & 1 deletion src/providers/virtual/qgsvirtuallayersqlitemodule.cpp
Expand Up @@ -695,11 +695,13 @@ int vtableColumn( sqlite3_vtab_cursor *cursor, sqlite3_context *ctxt, int idx )
switch ( v.type() )
{
case QVariant::Int:
case QVariant::UInt:
case QVariant::Bool:
// read signed integer
sqlite3_result_int( ctxt, v.toInt() );
break;
case QVariant::UInt:
case QVariant::LongLong:
// read 64 bits signed integer (or 32 bits unsigned one)
sqlite3_result_int64( ctxt, v.toLongLong() );
break;
case QVariant::Double:
Expand Down
32 changes: 29 additions & 3 deletions tests/src/python/test_provider_virtual.py
Expand Up @@ -610,14 +610,14 @@ def test_sql3(self):
l4 = QgsVectorLayer("?query=%s" % query, "tt", "virtual", QgsVectorLayer.LayerOptions(False))
self.assertEqual(l4.isValid(), True)
self.assertEqual(l4.dataProvider().fields().at(0).name(), "count(*)")
self.assertEqual(l4.dataProvider().fields().at(0).type(), QVariant.Int)
self.assertEqual(l4.dataProvider().fields().at(0).type(), QVariant.LongLong)

def test_sql_field_types(self):
query = toPercent("SELECT 42 as t, 'ok'||'ok' as t2, GeomFromText('') as t3, 3.14*2 as t4")
l4 = QgsVectorLayer("?query=%s" % query, "tt", "virtual", QgsVectorLayer.LayerOptions(False))
self.assertEqual(l4.isValid(), True)
self.assertEqual(l4.dataProvider().fields().at(0).name(), "t")
self.assertEqual(l4.dataProvider().fields().at(0).type(), QVariant.Int)
self.assertEqual(l4.dataProvider().fields().at(0).type(), QVariant.LongLong)
self.assertEqual(l4.dataProvider().fields().at(1).name(), "t2")
self.assertEqual(l4.dataProvider().fields().at(1).type(), QVariant.String)
self.assertEqual(l4.dataProvider().fields().at(2).name(), "t3")
Expand All @@ -635,7 +635,7 @@ def test_sql_field_types(self):
self.assertEqual(l4.dataProvider().fields().at(1).name(), "t2")
self.assertEqual(l4.dataProvider().fields().at(1).type(), QVariant.String)
self.assertEqual(l4.dataProvider().fields().at(2).name(), "t4")
self.assertEqual(l4.dataProvider().fields().at(2).type(), QVariant.Int)
self.assertEqual(l4.dataProvider().fields().at(2).type(), QVariant.LongLong)
self.assertEqual(l4.dataProvider().wkbType(), 4) # multipoint

# test value types (!= from declared column types)
Expand Down Expand Up @@ -1326,6 +1326,32 @@ def test_bool_fields(self):

QgsProject.instance().removeMapLayer(ml.id())

def test_int64(self):
"""
Test that 64 bits integer doesn't generate an integer overflow
"""
bigint = 2262000000

ml = QgsVectorLayer('NoGeometry?crs=epsg:4326&field=fldlonglong:long',
'test_bigint', 'memory')
provider = ml.dataProvider()
feat = QgsFeature(ml.fields())
feat.setAttribute('fldlonglong', bigint)
provider.addFeatures([feat])

self.assertEqual(ml.isValid(), True)
QgsProject.instance().addMapLayer(ml)

df = QgsVirtualLayerDefinition()
df.setQuery('select * from test_bigint')
vl = QgsVectorLayer(df.toString(), "testq", "virtual")
self.assertEqual(len(vl.fields()), 1)
field = vl.fields()[0]
self.assertEqual(field.type(), QVariant.LongLong)
self.assertTrue(vl.isValid())
feat = next(vl.getFeatures())
self.assertEqual(feat.attribute('fldlonglong'), bigint)


if __name__ == '__main__':
unittest.main()
3 changes: 2 additions & 1 deletion tests/src/python/test_qgsvirtuallayerdefinition.py
Expand Up @@ -80,9 +80,10 @@ def test1(self):
f.append(QgsField("f", QVariant.Double))
f.append(QgsField("s", QVariant.String))
d.setFields(f)

f2 = QgsVirtualLayerDefinition.fromUrl(d.toUrl()).fields()
self.assertEqual(f[0].name(), f2[0].name())
self.assertEqual(f[0].type(), f2[0].type())
self.assertEqual(f2[0].type(), QVariant.LongLong)
self.assertEqual(f[1].name(), f2[1].name())
self.assertEqual(f[1].type(), f2[1].type())
self.assertEqual(f[2].name(), f2[2].name())
Expand Down

0 comments on commit 67bf72c

Please sign in to comment.