1 /***************************************************************************
2       qgsmssqlgeometryparser.cpp  -  SqlGeometry parser for mssql server
3                              -------------------
4     begin                : 2011-10-08
5     copyright            : (C) 2011 by Tamas Szekeres
6     email                : szekerest at gmail.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "qgsmssqlgeometryparser.h"
19 #include "qgsgeometry.h"
20 #include "qgslogger.h"
21 #include "qgsapplication.h"
22 
23 /*   SqlGeometry/SqlGeography serialization format
24 
25 Simple Point (SerializationProps & IsSinglePoint)
26   [SRID][0x01][SerializationProps][Point][z][m]
27 
28 Simple Line Segment (SerializationProps & IsSingleLineSegment)
29   [SRID][0x01][SerializationProps][Point1][Point2][z1][z2][m1][m2]
30 
31 Complex Geometries
32   [SRID][VersionAttribute][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
33   [NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape]
34 
35 Complex Geometries (FigureAttribute == Curve)
36   [SRID][VersionAttribute][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
37   [NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape][NumSegments][SegmentType]..[SegmentType]
38 
39 VersionAttribute (1 byte)
40   0x01 = Katmai (MSSQL2008+)
41   0x02 = Denali (MSSQL2012+)
42 
43 SRID
44   Spatial Reference Id (4 bytes)
45 
46 SerializationProps (bitmask) 1 byte
47   0x01 = HasZValues
48   0x02 = HasMValues
49   0x04 = IsValid
50   0x08 = IsSinglePoint
51   0x10 = IsSingleLineSegment
52   0x20 = IsLargerThanAHemisphere
53 
54 Point (2-4)x8 bytes, size depends on SerializationProps & HasZValues & HasMValues
55   [x][y]                  - SqlGeometry
56   [latitude][longitude]   - SqlGeography
57 
58 Figure
59   [FigureAttribute][PointOffset]
60 
61 FigureAttribute - Katmai (1 byte)
62   0x00 = Interior Ring
63   0x01 = Stroke
64   0x02 = Exterior Ring
65 
66 FigureAttribute - Denali (1 byte)
67   0x00 = None
68   0x01 = Line
69   0x02 = Arc
70   0x03 = Curve
71 
72 Shape
73   [ParentFigureOffset][FigureOffset][ShapeType]
74 
75 ShapeType (1 byte)
76   0x00 = Unknown
77   0x01 = Point
78   0x02 = LineString
79   0x03 = Polygon
80   0x04 = MultiPoint
81   0x05 = MultiLineString
82   0x06 = MultiPolygon
83   0x07 = GeometryCollection
84   -- Denali
85   0x08 = CircularString
86   0x09 = CompoundCurve
87   0x0A = CurvePolygon
88   0x0B = FullGlobe
89 
90 SegmentType (1 byte)
91   0x00 = Line
92   0x01 = Arc
93   0x02 = FirstLine
94   0x03 = FirstArc
95 
96 */
97 
98 /************************************************************************/
99 /*                         Geometry parser macros                       */
100 /************************************************************************/
101 
102 #define VA_KATMAI 0x01
103 #define VA_DENALI 0x02
104 
105 #define SP_NONE 0
106 #define SP_HASZVALUES 1
107 #define SP_HASMVALUES 2
108 #define SP_ISVALID 4
109 #define SP_ISSINGLEPOINT 8
110 #define SP_ISSINGLELINESEGMENT 0x10
111 #define SP_ISLARGERTHANAHEMISPHERE 0x20
112 
113 #define ST_UNKNOWN 0
114 #define ST_POINT 1
115 #define ST_LINESTRING 2
116 #define ST_POLYGON 3
117 #define ST_MULTIPOINT 4
118 #define ST_MULTILINESTRING 5
119 #define ST_MULTIPOLYGON 6
120 #define ST_GEOMETRYCOLLECTION 7
121 #define ST_CIRCULARSTRING 8
122 #define ST_COMPOUNDCURVE 9
123 #define ST_CURVEPOLYGON 10
124 #define ST_FULLGLOBE 11
125 
126 #define FA_INTERIORRING 0x00
127 #define FA_STROKE 0x01
128 #define FA_EXTERIORRING 0x02
129 
130 #define FA_NONE 0x00
131 #define FA_LINE 0x01
132 #define FA_ARC 0x02
133 #define FA_CURVE 0x03
134 
135 #define SMT_LINE 0
136 #define SMT_ARC 1
137 #define SMT_FIRSTLINE 2
138 #define SMT_FIRSTARC 3
139 
140 #define ReadInt32(nPos) (*((unsigned int*)(mData + (nPos))))
141 
142 #define ReadByte(nPos) (mData[nPos])
143 
144 #define ReadDouble(nPos) (*((double*)(mData + (nPos))))
145 
146 #define ParentOffset(iShape) (ReadInt32(mShapePos + (iShape) * 9 ))
147 #define FigureOffset(iShape) (ReadInt32(mShapePos + (iShape) * 9 + 4))
148 #define ShapeType(iShape) (ReadByte(mShapePos + (iShape) * 9 + 8))
149 #define SegmentType(iSegment) (ReadByte(mSegmentPos + (iSegment)))
150 
151 #define NextFigureOffset(iShape) (iShape + 1 < mNumShapes? FigureOffset((iShape) +1) : mNumFigures)
152 
153 #define FigureAttribute(iFigure) (ReadByte(mFigurePos + (iFigure) * 5))
154 #define PointOffset(iFigure) (ReadInt32(mFigurePos + (iFigure) * 5 + 1))
155 #define NextPointOffset(iFigure) (iFigure + 1 < mNumFigures? PointOffset((iFigure) +1) : mNumPoints)
156 
157 #define ReadX(iPoint) (ReadDouble(mPointPos + 16 * (iPoint)))
158 #define ReadY(iPoint) (ReadDouble(mPointPos + 16 * (iPoint) + 8))
159 #define ReadZ(iPoint) (ReadDouble(mPointPos + 16 * mNumPoints + 8 * (iPoint)))
160 #define ReadM(iPoint) (ReadDouble(mPointPos + 24 * mNumPoints + 8 * (iPoint)))
161 
QgsMssqlGeometryParser()162 QgsMssqlGeometryParser::QgsMssqlGeometryParser()
163 {
164 }
165 
DumpMemoryToLog(const char * pszMsg,unsigned char * pszInput,int nLen)166 void QgsMssqlGeometryParser::DumpMemoryToLog( const char *pszMsg, unsigned char *pszInput, int nLen )
167 {
168 #if 0
169   char buf[55];
170   int len = 0;
171   QFile file( "qgsmssql.log" );
172   file.open( QIODevice::Append );
173   file.write( pszMsg, strlen( pszMsg ) );
174   file.write( "\n" );
175   sprintf( buf + len, "%05d ", 0 );
176   len += 6;
177   for ( int i = 0; i < nLen; i++ )
178   {
179     sprintf( buf + len, "%02x ", pszInput[i] );
180     len += 3;
181     if ( len == 54 )
182     {
183       file.write( buf, len );
184       len = 0;
185       file.write( "\n" );
186       sprintf( buf + len, "%05d ", i + 1 );
187       len = 6;
188     }
189   }
190   file.write( "\n" );
191   file.close();
192 #else
193   Q_UNUSED( pszMsg )
194   Q_UNUSED( pszInput )
195   Q_UNUSED( nLen )
196 #endif
197 }
198 
readCoordinates(int iPoint)199 QgsPoint QgsMssqlGeometryParser::readCoordinates( int iPoint )
200 {
201   if ( mIsGeography )
202   {
203     if ( ( mProps & SP_HASZVALUES ) && ( mProps & SP_HASMVALUES ) )
204       return QgsPoint( QgsWkbTypes::PointZM, ReadY( iPoint ), ReadX( iPoint ), ReadZ( iPoint ), ReadM( iPoint ) );
205     else if ( mProps & SP_HASZVALUES )
206       return QgsPoint( QgsWkbTypes::PointZ, ReadY( iPoint ), ReadX( iPoint ), ReadZ( iPoint ) );
207     else if ( mProps & SP_HASMVALUES )
208       return QgsPoint( QgsWkbTypes::PointM, ReadY( iPoint ), ReadX( iPoint ), 0.0, ReadZ( iPoint ) );
209     else
210       return QgsPoint( QgsWkbTypes::Point, ReadY( iPoint ), ReadX( iPoint ) );
211   }
212   else
213   {
214     if ( ( mProps & SP_HASZVALUES ) && ( mProps & SP_HASMVALUES ) )
215       return QgsPoint( QgsWkbTypes::PointZM, ReadX( iPoint ), ReadY( iPoint ), ReadZ( iPoint ), ReadM( iPoint ) );
216     else if ( mProps & SP_HASZVALUES )
217       return QgsPoint( QgsWkbTypes::PointZ, ReadX( iPoint ), ReadY( iPoint ), ReadZ( iPoint ) );
218     else if ( mProps & SP_HASMVALUES )
219       return QgsPoint( QgsWkbTypes::PointM, ReadX( iPoint ), ReadY( iPoint ), 0.0, ReadZ( iPoint ) );
220     else
221       return QgsPoint( QgsWkbTypes::Point, ReadX( iPoint ), ReadY( iPoint ) );
222   }
223 }
224 
readCoordinates(int iPoint,int iNextPoint,double * x,double * y,double * z,double * m)225 void QgsMssqlGeometryParser::readCoordinates( int iPoint, int iNextPoint, double *x, double *y, double *z, double *m )
226 {
227   int i = 0;
228   if ( mIsGeography )
229   {
230     if ( ( mProps & SP_HASZVALUES ) && ( mProps & SP_HASMVALUES ) )
231     {
232       while ( iPoint < iNextPoint )
233       {
234         x[i] = ReadY( iPoint );
235         y[i] = ReadX( iPoint );
236         z[i] = ReadZ( iPoint );
237         m[i] = ReadM( iPoint );
238         ++iPoint;
239         ++i;
240       }
241     }
242     else if ( mProps & SP_HASZVALUES )
243     {
244       while ( iPoint < iNextPoint )
245       {
246         x[i] = ReadY( iPoint );
247         y[i] = ReadX( iPoint );
248         z[i] = ReadZ( iPoint );
249         ++iPoint;
250         ++i;
251       }
252     }
253     else if ( mProps & SP_HASMVALUES )
254     {
255       while ( iPoint < iNextPoint )
256       {
257         x[i] = ReadY( iPoint );
258         y[i] = ReadX( iPoint );
259         m[i] = ReadZ( iPoint );
260         ++iPoint;
261         ++i;
262       }
263     }
264     else
265     {
266       while ( iPoint < iNextPoint )
267       {
268         x[i] = ReadY( iPoint );
269         y[i] = ReadX( iPoint );
270         ++iPoint;
271         ++i;
272       }
273     }
274   }
275   else
276   {
277     if ( ( mProps & SP_HASZVALUES ) && ( mProps & SP_HASMVALUES ) )
278     {
279       while ( iPoint < iNextPoint )
280       {
281         x[i] = ReadX( iPoint );
282         y[i] = ReadY( iPoint );
283         z[i] = ReadZ( iPoint );
284         m[i] = ReadM( iPoint );
285         ++iPoint;
286         ++i;
287       }
288     }
289     else if ( mProps & SP_HASZVALUES )
290     {
291       while ( iPoint < iNextPoint )
292       {
293         x[i] = ReadX( iPoint );
294         y[i] = ReadY( iPoint );
295         z[i] = ReadZ( iPoint );
296         ++iPoint;
297         ++i;
298       }
299     }
300     else if ( mProps & SP_HASMVALUES )
301     {
302       while ( iPoint < iNextPoint )
303       {
304         x[i] = ReadX( iPoint );
305         y[i] = ReadY( iPoint );
306         m[i] = ReadZ( iPoint );
307         ++iPoint;
308         ++i;
309       }
310     }
311     else
312     {
313       while ( iPoint < iNextPoint )
314       {
315         x[i] = ReadX( iPoint );
316         y[i] = ReadY( iPoint );
317         ++iPoint;
318         ++i;
319       }
320     }
321   }
322 }
323 
readPointSequence(int iPoint,int iNextPoint)324 const QgsPointSequence QgsMssqlGeometryParser::readPointSequence( int iPoint, int iNextPoint )
325 {
326   if ( iPoint >= iNextPoint )
327     return QgsPointSequence();
328 
329   QgsPointSequence pts;
330 
331   while ( iPoint < iNextPoint )
332   {
333     pts << readCoordinates( iPoint );
334     ++iPoint;
335   }
336 
337   return pts;
338 }
339 
readPoint(int iFigure)340 std::unique_ptr< QgsPoint > QgsMssqlGeometryParser::readPoint( int iFigure )
341 {
342   if ( iFigure < mNumFigures )
343   {
344     int iPoint = PointOffset( iFigure );
345     if ( iPoint < mNumPoints )
346     {
347       return qgis::make_unique< QgsPoint >( readCoordinates( iPoint ) );
348     }
349   }
350   return nullptr;
351 }
352 
readMultiPoint(int iShape)353 std::unique_ptr< QgsMultiPoint > QgsMssqlGeometryParser::readMultiPoint( int iShape )
354 {
355   std::unique_ptr< QgsMultiPoint > poMultiPoint = qgis::make_unique< QgsMultiPoint >();
356   poMultiPoint->reserve( mNumShapes );
357   for ( int i = iShape + 1; i < mNumShapes; i++ )
358   {
359     if ( ParentOffset( i ) == ( unsigned int )iShape )
360     {
361       if ( ShapeType( i ) == ST_POINT )
362         poMultiPoint->addGeometry( readPoint( FigureOffset( i ) ).release() );
363     }
364   }
365 
366   return poMultiPoint;
367 }
368 
readLineString(int iPoint,int iNextPoint)369 std::unique_ptr< QgsLineString > QgsMssqlGeometryParser::readLineString( int iPoint, int iNextPoint )
370 {
371   QVector< double > xOut( iNextPoint - iPoint );
372   QVector< double > yOut( iNextPoint - iPoint );
373   QVector< double > zOut;
374   if ( mProps & SP_HASZVALUES )
375     zOut.resize( iNextPoint - iPoint );
376   QVector< double > mOut;
377   if ( mProps & SP_HASMVALUES )
378     mOut.resize( iNextPoint - iPoint );
379   double *x = xOut.data();
380   double *y = yOut.data();
381   double *z = zOut.data();
382   double *m = mOut.data();
383 
384   readCoordinates( iPoint, iNextPoint, x, y, z, m );
385 
386   return qgis::make_unique< QgsLineString >( xOut, yOut, zOut, mOut );
387 }
388 
readLineString(int iFigure)389 std::unique_ptr< QgsLineString > QgsMssqlGeometryParser::readLineString( int iFigure )
390 {
391   return readLineString( PointOffset( iFigure ), NextPointOffset( iFigure ) );
392 }
393 
readCircularString(int iPoint,int iNextPoint)394 std::unique_ptr< QgsCircularString > QgsMssqlGeometryParser::readCircularString( int iPoint, int iNextPoint )
395 {
396   std::unique_ptr< QgsCircularString > poCircularString = qgis::make_unique< QgsCircularString >();
397   poCircularString->setPoints( readPointSequence( iPoint, iNextPoint ) );
398   return poCircularString;
399 }
400 
readCircularString(int iFigure)401 std::unique_ptr< QgsCircularString > QgsMssqlGeometryParser::readCircularString( int iFigure )
402 {
403   return readCircularString( PointOffset( iFigure ), NextPointOffset( iFigure ) );
404 }
405 
readMultiLineString(int iShape)406 std::unique_ptr< QgsMultiLineString > QgsMssqlGeometryParser::readMultiLineString( int iShape )
407 {
408   std::unique_ptr< QgsMultiLineString > poMultiLineString = qgis::make_unique< QgsMultiLineString >();
409   poMultiLineString->reserve( mNumShapes );
410   for ( int i = iShape + 1; i < mNumShapes; i++ )
411   {
412     if ( ParentOffset( i ) == ( unsigned int )iShape )
413     {
414       if ( ShapeType( i ) == ST_LINESTRING )
415         poMultiLineString->addGeometry( readLineString( FigureOffset( i ) ).release() );
416     }
417   }
418 
419   return poMultiLineString;
420 }
421 
readPolygon(int iShape)422 std::unique_ptr< QgsPolygon > QgsMssqlGeometryParser::readPolygon( int iShape )
423 {
424   int iFigure;
425   int iRingCount = 0;
426   int iNextFigure = NextFigureOffset( iShape );
427 
428   std::unique_ptr< QgsPolygon > poPoly = qgis::make_unique< QgsPolygon >();
429   for ( iFigure = FigureOffset( iShape ); iFigure < iNextFigure; iFigure++ )
430   {
431     if ( iRingCount == 0 )
432       poPoly->setExteriorRing( readLineString( iFigure ).release() );
433     else
434       poPoly->addInteriorRing( readLineString( iFigure ).release() );
435 
436     ++iRingCount;
437   }
438   return poPoly;
439 }
440 
readMultiPolygon(int iShape)441 std::unique_ptr< QgsMultiPolygon > QgsMssqlGeometryParser::readMultiPolygon( int iShape )
442 {
443   std::unique_ptr< QgsMultiPolygon > poMultiPolygon = qgis::make_unique< QgsMultiPolygon >();
444   poMultiPolygon->reserve( mNumShapes );
445   for ( int i = iShape + 1; i < mNumShapes; i++ )
446   {
447     if ( ParentOffset( i ) == ( unsigned int )iShape )
448     {
449       if ( ShapeType( i ) == ST_POLYGON )
450         poMultiPolygon->addGeometry( readPolygon( i ).release() );
451     }
452   }
453 
454   return poMultiPolygon;
455 }
456 
readCompoundCurve(int iFigure)457 std::unique_ptr< QgsCompoundCurve > QgsMssqlGeometryParser::readCompoundCurve( int iFigure )
458 {
459   int iPoint, iNextPoint, nPointsPrepared;
460   std::unique_ptr< QgsCompoundCurve > poCompoundCurve = qgis::make_unique< QgsCompoundCurve >();
461   iPoint = PointOffset( iFigure );
462   iNextPoint = NextPointOffset( iFigure ) - 1;
463 
464   std::unique_ptr< QgsCurve > poGeom;
465 
466   nPointsPrepared = 0;
467   bool isCurve = false;
468   while ( iPoint < iNextPoint && mSegment < mNumSegments )
469   {
470     switch ( SegmentType( mSegment ) )
471     {
472       case SMT_FIRSTLINE:
473         if ( nPointsPrepared > 0 )
474         {
475           if ( isCurve )
476             poCompoundCurve->addCurve( readCircularString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
477           else
478             poCompoundCurve->addCurve( readLineString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
479         }
480         isCurve = false;
481         nPointsPrepared = 1;
482         ++iPoint;
483         break;
484       case SMT_LINE:
485         ++nPointsPrepared;
486         ++iPoint;
487         break;
488       case SMT_FIRSTARC:
489         if ( nPointsPrepared > 0 )
490         {
491           if ( isCurve )
492             poCompoundCurve->addCurve( readCircularString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
493           else
494             poCompoundCurve->addCurve( readLineString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
495         }
496         isCurve = true;
497         nPointsPrepared = 2;
498         iPoint += 2;
499         break;
500       case SMT_ARC:
501         nPointsPrepared += 2;
502         iPoint += 2;
503         break;
504     }
505     ++mSegment;
506   }
507 
508   // adding the last curve
509   if ( iPoint == iNextPoint )
510   {
511     if ( isCurve )
512       poCompoundCurve->addCurve( readCircularString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
513     else
514       poCompoundCurve->addCurve( readLineString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
515   }
516 
517   return poCompoundCurve;
518 }
519 
readCurvePolygon(int iShape)520 std::unique_ptr< QgsCurvePolygon > QgsMssqlGeometryParser::readCurvePolygon( int iShape )
521 {
522   int iFigure;
523   int iRingCount = 0;
524   int iNextFigure = NextFigureOffset( iShape );
525 
526   std::unique_ptr< QgsCurvePolygon > poPoly = qgis::make_unique< QgsCurvePolygon >();
527   for ( iFigure = FigureOffset( iShape ); iFigure < iNextFigure; iFigure++ )
528   {
529     switch ( FigureAttribute( iFigure ) )
530     {
531       case FA_LINE:
532         if ( iRingCount == 0 )
533           poPoly->setExteriorRing( readLineString( iFigure ).release() );
534         else
535           poPoly->addInteriorRing( readLineString( iFigure ).release() );
536         break;
537       case FA_ARC:
538         if ( iRingCount == 0 )
539           poPoly->setExteriorRing( readCircularString( iFigure ).release() );
540         else
541           poPoly->addInteriorRing( readCircularString( iFigure ).release() );
542         break;
543       case FA_CURVE:
544         if ( iRingCount == 0 )
545           poPoly->setExteriorRing( readCompoundCurve( iFigure ).release() );
546         else
547           poPoly->addInteriorRing( readCompoundCurve( iFigure ).release() );
548         break;
549     }
550     ++iRingCount;
551   }
552   return poPoly;
553 }
554 
readGeometryCollection(int iShape)555 std::unique_ptr< QgsGeometryCollection > QgsMssqlGeometryParser::readGeometryCollection( int iShape )
556 {
557   std::unique_ptr< QgsGeometryCollection> poGeomColl = qgis::make_unique< QgsGeometryCollection >();
558   poGeomColl->reserve( mNumShapes );
559   for ( int i = iShape + 1; i < mNumShapes; i++ )
560   {
561     if ( ParentOffset( i ) == ( unsigned int )iShape )
562     {
563       switch ( ShapeType( i ) )
564       {
565         case ST_POINT:
566           poGeomColl->addGeometry( readPoint( FigureOffset( i ) ).release() );
567           break;
568         case ST_LINESTRING:
569           poGeomColl->addGeometry( readLineString( FigureOffset( i ) ).release() );
570           break;
571         case ST_POLYGON:
572           poGeomColl->addGeometry( readPolygon( i ).release() );
573           break;
574         case ST_MULTIPOINT:
575           poGeomColl->addGeometry( readMultiPoint( i ).release() );
576           break;
577         case ST_MULTILINESTRING:
578           poGeomColl->addGeometry( readMultiLineString( i ).release() );
579           break;
580         case ST_MULTIPOLYGON:
581           poGeomColl->addGeometry( readMultiPolygon( i ).release() );
582           break;
583         case ST_GEOMETRYCOLLECTION:
584           poGeomColl->addGeometry( readGeometryCollection( i ).release() );
585           break;
586         case ST_CIRCULARSTRING:
587           poGeomColl->addGeometry( readCircularString( FigureOffset( i ) ).release() );
588           break;
589         case ST_COMPOUNDCURVE:
590           poGeomColl->addGeometry( readCompoundCurve( FigureOffset( i ) ).release() );
591           break;
592         case ST_CURVEPOLYGON:
593           poGeomColl->addGeometry( readCurvePolygon( i ).release() );
594           break;
595       }
596     }
597   }
598 
599   return poGeomColl;
600 }
601 
parseSqlGeometry(unsigned char * pszInput,int nLen)602 std::unique_ptr<QgsAbstractGeometry> QgsMssqlGeometryParser::parseSqlGeometry( unsigned char *pszInput, int nLen )
603 {
604   if ( nLen < 10 )
605   {
606     QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
607     DumpMemoryToLog( "Not enough data", pszInput, nLen );
608     return nullptr;
609   }
610 
611   mData = pszInput;
612 
613   /* store the SRS id for further use */
614   mSRSId = ReadInt32( 0 );
615 
616   mVersion = ReadByte( 4 );
617 
618   if ( mVersion == 0 || mVersion > 2 )
619   {
620     QgsDebugMsg( QStringLiteral( "ParseSqlGeometry corrupt data" ) );
621     DumpMemoryToLog( "Corrupt data", pszInput, nLen );
622     return nullptr;
623   }
624 
625   mProps = ReadByte( 5 );
626 
627   if ( mProps & SP_HASZVALUES && mProps & SP_HASMVALUES )
628     mPointSize = 32;
629   else if ( mProps & SP_HASZVALUES || mProps & SP_HASMVALUES )
630     mPointSize = 24;
631   else
632     mPointSize = 16;
633 
634   std::unique_ptr< QgsAbstractGeometry> poGeom;
635 
636   if ( mProps & SP_ISSINGLEPOINT )
637   {
638     // single point geometry
639     mNumPoints = 1;
640     mPointPos = 6;
641 
642     if ( nLen < 6 + mPointSize )
643     {
644       QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
645       DumpMemoryToLog( "Not enough data", pszInput, nLen );
646       return nullptr;
647     }
648 
649     poGeom = qgis::make_unique< QgsPoint >( readCoordinates( 0 ) );
650   }
651   else if ( mProps & SP_ISSINGLELINESEGMENT )
652   {
653     // single line segment with 2 points
654     mNumPoints = 2;
655     mPointPos = 6;
656 
657     if ( nLen < 6 + 2 * mPointSize )
658     {
659       QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
660       DumpMemoryToLog( "Not enough data", pszInput, nLen );
661       return nullptr;
662     }
663 
664     poGeom = qgis::make_unique< QgsLineString >( readCoordinates( 0 ), readCoordinates( 1 ) );
665   }
666   else
667   {
668     // complex geometries
669     mNumPoints = ReadInt32( 6 );
670 
671     if ( mNumPoints <= 0 )
672     {
673       return nullptr;
674     }
675 
676     // position of the point array
677     mPointPos = 10;
678 
679     // position of the figures
680     mFigurePos = mPointPos + mPointSize * mNumPoints + 4;
681 
682     if ( nLen < mFigurePos )
683     {
684       QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
685       DumpMemoryToLog( "Not enough data", pszInput, nLen );
686       return nullptr;
687     }
688 
689     mNumFigures = ReadInt32( mFigurePos - 4 );
690 
691     if ( mNumFigures <= 0 )
692     {
693       return nullptr;
694     }
695 
696     // position of the shapes
697     mShapePos = mFigurePos + 5 * mNumFigures + 4;
698 
699     if ( nLen < mShapePos )
700     {
701       QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
702       DumpMemoryToLog( "Not enough data", pszInput, nLen );
703       return nullptr;
704     }
705 
706     mNumShapes = ReadInt32( mShapePos - 4 );
707 
708     if ( nLen < mShapePos + 9 * mNumShapes )
709     {
710       QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
711       DumpMemoryToLog( "Not enough data", pszInput, nLen );
712       return nullptr;
713     }
714 
715     if ( mNumShapes <= 0 )
716     {
717       return nullptr;
718     }
719 
720     // position of the segments (for complex curve figures)
721     if ( mVersion == 0x02 )
722     {
723       mSegment = 0;
724       mSegmentPos = mShapePos + 9 * mNumShapes + 4;
725       if ( nLen > mSegmentPos )
726       {
727         // segment array is present
728         mNumSegments = ReadInt32( mSegmentPos - 4 );
729         if ( nLen < mSegmentPos + mNumSegments )
730         {
731           QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
732           DumpMemoryToLog( "Not enough data", pszInput, nLen );
733           return nullptr;
734         }
735       }
736     }
737 
738     // pick up the root shape
739     if ( ParentOffset( 0 ) != 0xFFFFFFFF )
740     {
741       QgsDebugMsg( QStringLiteral( "ParseSqlGeometry corrupt data" ) );
742       DumpMemoryToLog( "Not enough data", pszInput, nLen );
743       return nullptr;
744     }
745 
746     // determine the shape type
747     switch ( ShapeType( 0 ) )
748     {
749       case ST_POINT:
750         poGeom = readPoint( FigureOffset( 0 ) );
751         break;
752       case ST_LINESTRING:
753         poGeom = readLineString( FigureOffset( 0 ) );
754         break;
755       case ST_POLYGON:
756         poGeom = readPolygon( 0 );
757         break;
758       case ST_MULTIPOINT:
759         poGeom = readMultiPoint( 0 );
760         break;
761       case ST_MULTILINESTRING:
762         poGeom = readMultiLineString( 0 );
763         break;
764       case ST_MULTIPOLYGON:
765         poGeom = readMultiPolygon( 0 );
766         break;
767       case ST_GEOMETRYCOLLECTION:
768         poGeom = readGeometryCollection( 0 );
769         break;
770       case ST_CIRCULARSTRING:
771         poGeom = readCircularString( FigureOffset( 0 ) );
772         break;
773       case ST_COMPOUNDCURVE:
774         poGeom = readCompoundCurve( FigureOffset( 0 ) );
775         break;
776       case ST_CURVEPOLYGON:
777         poGeom = readCurvePolygon( 0 );
778         break;
779       default:
780         QgsDebugMsg( QStringLiteral( "ParseSqlGeometry unsupported geometry type" ) );
781         DumpMemoryToLog( "Unsupported geometry type", pszInput, nLen );
782         return nullptr;
783     }
784   }
785 
786   return poGeom;
787 }
788 
789