Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: MSSQL Spatial driver
4 : * Purpose: Definition of classes for OGR MSSQL Spatial driver.
5 : * Author: Tamas Szekeres, szekerest at gmail.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010, Tamas Szekeres
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #ifndef OGR_MSSQLSPATIAL_H_INCLUDED
14 : #define OGR_MSSQLSPATIAL_H_INCLUDED
15 :
16 : #include "ogrsf_frmts.h"
17 : #include "cpl_odbc.h"
18 : #include "cpl_error.h"
19 :
20 : #ifdef SQLNCLI_VERSION
21 : #include <sqlncli.h>
22 : #endif
23 : #ifdef MSODBCSQL_VERSION
24 : #include "include_msodbcsql.h"
25 : #endif
26 :
27 : #include <map>
28 :
29 : class OGRMSSQLSpatialDataSource;
30 :
31 : /* layer status */
32 : #define MSSQLLAYERSTATUS_ORIGINAL 0
33 : #define MSSQLLAYERSTATUS_INITIAL 1
34 : #define MSSQLLAYERSTATUS_CREATED 2
35 : #define MSSQLLAYERSTATUS_DISABLED 3
36 :
37 : /* geometry format to transfer geometry column */
38 : #define MSSQLGEOMETRY_NATIVE 0
39 : #define MSSQLGEOMETRY_WKB 1
40 : #define MSSQLGEOMETRY_WKT 2
41 : #define MSSQLGEOMETRY_WKBZM 3 /* SQL Server 2012 */
42 :
43 : /* geometry column types */
44 : #define MSSQLCOLTYPE_GEOMETRY 0
45 : #define MSSQLCOLTYPE_GEOGRAPHY 1
46 : #define MSSQLCOLTYPE_BINARY 2
47 : #define MSSQLCOLTYPE_TEXT 3
48 :
49 : /* sqlgeometry constants */
50 :
51 : #define VA_KATMAI 0x01
52 : #define VA_DENALI 0x02
53 :
54 : #define SP_NONE 0
55 : #define SP_HASZVALUES 1
56 : #define SP_HASMVALUES 2
57 : #define SP_ISVALID 4
58 : #define SP_ISSINGLEPOINT 8
59 : #define SP_ISSINGLELINESEGMENT 0x10
60 : #define SP_ISLARGERTHANAHEMISPHERE 0x20
61 :
62 : #define ST_UNKNOWN 0
63 : #define ST_POINT 1
64 : #define ST_LINESTRING 2
65 : #define ST_POLYGON 3
66 : #define ST_MULTIPOINT 4
67 : #define ST_MULTILINESTRING 5
68 : #define ST_MULTIPOLYGON 6
69 : #define ST_GEOMETRYCOLLECTION 7
70 : #define ST_CIRCULARSTRING 8
71 : #define ST_COMPOUNDCURVE 9
72 : #define ST_CURVEPOLYGON 10
73 : #define ST_FULLGLOBE 11
74 :
75 : #define FA_INTERIORRING 0x00
76 : #define FA_STROKE 0x01
77 : #define FA_EXTERIORRING 0x02
78 :
79 : #define FA_NONE 0x00
80 : #define FA_LINE 0x01
81 : #define FA_ARC 0x02
82 : #define FA_CURVE 0x03
83 :
84 : #define SMT_LINE 0
85 : #define SMT_ARC 1
86 : #define SMT_FIRSTLINE 2
87 : #define SMT_FIRSTARC 3
88 :
89 : /************************************************************************/
90 : /* OGRMSSQLAppendEscaped( ) */
91 : /************************************************************************/
92 :
93 : void OGRMSSQLAppendEscaped(CPLODBCStatement *poStatement,
94 : const char *pszStrValue);
95 :
96 : /************************************************************************/
97 : /* OGRMSSQLGeometryParser */
98 : /************************************************************************/
99 :
100 : class OGRMSSQLGeometryValidator
101 : {
102 : protected:
103 : const OGRGeometry *const poOriginalGeometry;
104 : const int nGeomColumnType;
105 : const bool bIsValid;
106 : std::unique_ptr<OGRGeometry> poValidGeometry{};
107 :
108 : public:
109 : explicit OGRMSSQLGeometryValidator(const OGRGeometry *poGeom,
110 : int nGeomColumnType);
111 :
112 : bool IsValidLatLon(double longitude, double latitude);
113 : bool IsValidCircularZ(double z1, double z2);
114 : bool IsValidPolygonRingCount(const OGRCurve *poGeom);
115 : bool IsValidPolygonRingClosed(const OGRCurve *poGeom);
116 : bool IsValid(const OGRPoint *poGeom);
117 : bool IsValid(const OGRMultiPoint *poGeom);
118 : bool IsValid(const OGRCircularString *poGeom);
119 : bool IsValid(const OGRSimpleCurve *poGeom);
120 : bool IsValid(const OGRCompoundCurve *poGeom);
121 : bool IsValid(const OGRMultiLineString *poGeom);
122 : bool IsValid(const OGRCurvePolygon *poGeom);
123 : bool IsValid(const OGRMultiPolygon *poGeom);
124 : bool IsValid(const OGRGeometryCollection *poGeom);
125 : bool IsValid(const OGRGeometry *poGeom);
126 : void MakeValid(OGRPoint *poGeom);
127 : void MakeValid(OGRMultiPoint *poGeom);
128 : void MakeValid(OGRCircularString *poGeom);
129 : void MakeValid(OGRSimpleCurve *poGeom);
130 : void MakeValid(OGRCompoundCurve *poGeom);
131 : void MakeValid(OGRMultiLineString *poGeom);
132 : void MakeValid(OGRPolygon *poGeom);
133 : void MakeValid(OGRCurvePolygon *poGeom);
134 : void MakeValid(OGRMultiPolygon *poGeom);
135 : void MakeValid(OGRGeometryCollection *poGeom);
136 : void MakeValid(OGRGeometry *poGeom);
137 : bool ValidateGeometry(OGRGeometry *poGeom);
138 :
139 : const OGRGeometry *GetValidGeometryRef() const;
140 :
141 0 : bool IsValid() const
142 : {
143 0 : return bIsValid;
144 : }
145 : };
146 :
147 : /************************************************************************/
148 : /* OGRMSSQLGeometryParser */
149 : /************************************************************************/
150 :
151 : class OGRMSSQLGeometryParser
152 : {
153 : protected:
154 : const unsigned char *pszData;
155 : /* version information */
156 : char chVersion;
157 : /* serialization properties */
158 : char chProps;
159 : /* point array */
160 : int nPointSize;
161 : int nPointPos;
162 : int nNumPoints;
163 : /* figure array */
164 : int nFigurePos;
165 : int nNumFigures;
166 : /* shape array */
167 : int nShapePos;
168 : int nNumShapes;
169 : /* segmenttype array */
170 : int nSegmentPos;
171 : int nNumSegments;
172 : int iSegment;
173 : int nSRSId;
174 : /* geometry or geography */
175 : int nColType;
176 :
177 : protected:
178 : OGRPoint *ReadPoint(int iFigure);
179 : OGRMultiPoint *ReadMultiPoint(int iShape);
180 : OGRErr ReadSimpleCurve(OGRSimpleCurve *poCurve, int iPoint, int iNextPoint);
181 : OGRLineString *ReadLineString(int iFigure);
182 : OGRLinearRing *ReadLinearRing(int iFigure);
183 : OGRMultiLineString *ReadMultiLineString(int iShape);
184 : OGRPolygon *ReadPolygon(int iShape);
185 : OGRMultiPolygon *ReadMultiPolygon(int iShape);
186 : OGRGeometryCollection *ReadGeometryCollection(int iShape);
187 : OGRCircularString *ReadCircularString(int iFigure);
188 : OGRCompoundCurve *ReadCompoundCurve(int iFigure);
189 : void AddCurveSegment(OGRCompoundCurve *poCompoundCurve,
190 : OGRSimpleCurve *poCurve, int iPoint, int iNextPoint);
191 : OGRCurvePolygon *ReadCurvePolygon(int iShape);
192 :
193 : public:
194 : explicit OGRMSSQLGeometryParser(int nGeomColumnType);
195 : OGRErr ParseSqlGeometry(const unsigned char *pszInput, int nLen,
196 : OGRGeometry **poGeom);
197 :
198 0 : int GetSRSId()
199 : {
200 0 : return nSRSId;
201 : }
202 : };
203 :
204 : /************************************************************************/
205 : /* OGRMSSQLGeometryWriter */
206 : /************************************************************************/
207 :
208 : class OGRMSSQLGeometryWriter
209 : {
210 : protected:
211 : OGRGeometry *poGeom2;
212 : unsigned char *pszData;
213 : int nLen;
214 : /* version information */
215 : char chVersion;
216 : /* serialization properties */
217 : char chProps;
218 : /* point array */
219 : int nPointSize;
220 : int nPointPos;
221 : int nNumPoints;
222 : int iPoint;
223 : /* figure array */
224 : int nFigurePos;
225 : int nNumFigures;
226 : int iFigure;
227 : /* shape array */
228 : int nShapePos;
229 : int nNumShapes;
230 : int iShape;
231 : /* segmenttype array */
232 : int nSegmentPos;
233 : int nNumSegments;
234 : int iSegment;
235 : int nSRSId;
236 : /* geometry or geography */
237 : int nColType;
238 :
239 : protected:
240 : void WritePoint(OGRPoint *poGeom);
241 : void WritePoint(double x, double y);
242 : void WritePoint(double x, double y, double z);
243 : void WritePoint(double x, double y, double z, double m);
244 : void WriteSimpleCurve(OGRSimpleCurve *poGeom, bool bReversePoints);
245 : void WriteSimpleCurve(OGRSimpleCurve *poGeom, int iStartIndex,
246 : bool bReversePoints);
247 : void WriteSimpleCurve(OGRSimpleCurve *poGeom, int iStartIndex, int nCount,
248 : bool bReversePoints);
249 : void WriteCompoundCurve(OGRCompoundCurve *poGeom);
250 : void WriteCurve(OGRCurve *poGeom, bool bReversePoints);
251 : void WritePolygon(OGRPolygon *poGeom);
252 : void WriteCurvePolygon(OGRCurvePolygon *poGeom);
253 : void WriteGeometryCollection(OGRGeometryCollection *poGeom, int iParent);
254 : void WriteGeometry(OGRGeometry *poGeom, int iParent);
255 : void TrackGeometry(OGRGeometry *poGeom);
256 :
257 : public:
258 : OGRMSSQLGeometryWriter(OGRGeometry *poGeometry, int nGeomColumnType,
259 : int nSRS);
260 : OGRErr WriteSqlGeometry(unsigned char *pszBuffer, int nBufLen);
261 :
262 0 : int GetDataLen()
263 : {
264 0 : return nLen;
265 : }
266 : };
267 :
268 : /************************************************************************/
269 : /* OGRMSSQLSpatialLayer */
270 : /************************************************************************/
271 :
272 : class OGRMSSQLSpatialLayer CPL_NON_FINAL : public OGRLayer
273 : {
274 : protected:
275 : OGRFeatureDefn *poFeatureDefn = nullptr;
276 : int nRawColumns = 0;
277 :
278 : CPLODBCStatement *poStmt = nullptr;
279 : bool m_bEOF = false;
280 : bool m_bResetNeeded = false;
281 :
282 : // Layer spatial reference system, and srid.
283 : OGRSpatialReference *poSRS = nullptr;
284 : int nSRSId = 0;
285 :
286 : GIntBig iNextShapeId = 0;
287 :
288 : OGRMSSQLSpatialDataSource *poDS = nullptr;
289 :
290 : int nGeomColumnType = -1;
291 : char *pszGeomColumn = nullptr;
292 : int nGeomColumnIndex = -1;
293 : char *pszFIDColumn = nullptr;
294 : int nFIDColumnIndex = -1;
295 :
296 : // UUID doesn't work for now in bulk copy mode
297 : bool m_bHasUUIDColumn = false;
298 :
299 : int bIsIdentityFid = FALSE;
300 :
301 : int nLayerStatus = MSSQLLAYERSTATUS_ORIGINAL;
302 :
303 : int *panFieldOrdinals = nullptr;
304 :
305 : void BuildFeatureDefn(const char *pszLayerName, CPLODBCStatement *poStmt);
306 :
307 0 : virtual CPLODBCStatement *GetStatement()
308 : {
309 0 : return poStmt;
310 : }
311 :
312 : void ClearStatement();
313 : OGRFeature *GetNextRawFeature();
314 : bool bLayerDefnNeedsRefresh = false;
315 :
316 : public:
317 : explicit OGRMSSQLSpatialLayer(OGRMSSQLSpatialDataSource *);
318 : virtual ~OGRMSSQLSpatialLayer();
319 :
320 : virtual void ResetReading() override;
321 : virtual OGRFeature *GetNextFeature() override;
322 :
323 : virtual OGRFeature *GetFeature(GIntBig nFeatureId) override;
324 :
325 0 : virtual OGRFeatureDefn *GetLayerDefn() override
326 : {
327 0 : return poFeatureDefn;
328 : }
329 :
330 : virtual OGRSpatialReference *GetSpatialRef() override;
331 :
332 : virtual OGRErr StartTransaction() override;
333 : virtual OGRErr CommitTransaction() override;
334 : virtual OGRErr RollbackTransaction() override;
335 :
336 : virtual const char *GetFIDColumn() override;
337 : virtual const char *GetGeometryColumn() override;
338 :
339 : virtual int TestCapability(const char *) override;
340 : static char *GByteArrayToHexString(const GByte *pabyData, int nLen);
341 :
342 0 : void SetLayerStatus(int nStatus)
343 : {
344 0 : nLayerStatus = nStatus;
345 0 : }
346 :
347 0 : int GetLayerStatus()
348 : {
349 0 : return nLayerStatus;
350 : }
351 :
352 : GDALDataset *GetDataset() override;
353 : };
354 :
355 : /************************************************************************/
356 : /* OGRMSSQLSpatialTableLayer */
357 : /************************************************************************/
358 :
359 : typedef union
360 : {
361 : struct
362 : {
363 : int iIndicator;
364 : int Value;
365 : } Integer;
366 :
367 : struct
368 : {
369 : int iIndicator;
370 : GIntBig Value;
371 : } Integer64;
372 :
373 : struct
374 : {
375 : int iIndicator;
376 : double Value;
377 : } Float;
378 :
379 : struct
380 : {
381 : SQLLEN nSize;
382 : char *pData[8000];
383 : } VarChar;
384 :
385 : struct
386 : {
387 : SQLLEN nSize;
388 : GByte *pData;
389 : } RawData;
390 :
391 : } BCPData;
392 :
393 : class OGRMSSQLSpatialTableLayer final : public OGRMSSQLSpatialLayer
394 : {
395 : bool bUpdateAccess = true;
396 : bool bUseGeometryValidation = false;
397 : int bLaunderColumnNames = FALSE;
398 : int bPreservePrecision = FALSE;
399 : int bNeedSpatialIndex = FALSE;
400 : int bUseCopy = FALSE;
401 : int nBCPSize = 1000;
402 :
403 : #ifdef SQL_SS_UDT
404 : int nUploadGeometryFormat = MSSQLGEOMETRY_NATIVE;
405 : #else
406 : int nUploadGeometryFormat = MSSQLGEOMETRY_WKB;
407 : #endif
408 :
409 : char *pszQuery = nullptr;
410 :
411 : SQLHANDLE hEnvBCP = nullptr;
412 : #ifdef MSSQL_BCP_SUPPORTED
413 : SQLHANDLE hDBCBCP = nullptr;
414 : int nBCPCount = 0;
415 : BCPData **papstBindBuffer = nullptr;
416 :
417 : int bIdentityInsert = FALSE;
418 : #endif
419 :
420 : CPLODBCStatement *BuildStatement(const char *pszColumns);
421 :
422 : CPLString BuildFields();
423 :
424 : virtual CPLODBCStatement *GetStatement() override;
425 :
426 : char *pszTableName = nullptr;
427 : char *pszLayerName = nullptr;
428 : char *pszSchemaName = nullptr;
429 :
430 : OGRwkbGeometryType eGeomType = wkbNone;
431 :
432 : public:
433 : explicit OGRMSSQLSpatialTableLayer(OGRMSSQLSpatialDataSource *);
434 : virtual ~OGRMSSQLSpatialTableLayer();
435 :
436 : CPLErr Initialize(const char *pszSchema, const char *pszTableName,
437 : const char *pszGeomCol, int nCoordDimension, int nSRId,
438 : const char *pszSRText, OGRwkbGeometryType eType);
439 :
440 : OGRErr CreateSpatialIndex();
441 : void DropSpatialIndex();
442 :
443 : virtual OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
444 : bool bForce) override;
445 :
446 : virtual GIntBig GetFeatureCount(int) override;
447 :
448 : virtual OGRFeatureDefn *GetLayerDefn() override;
449 :
450 : virtual const char *GetName() override;
451 :
452 : virtual OGRErr SetAttributeFilter(const char *) override;
453 : virtual OGRFeature *GetNextFeature() override;
454 :
455 : virtual OGRErr ISetFeature(OGRFeature *poFeature) override;
456 : virtual OGRErr DeleteFeature(GIntBig nFID) override;
457 : virtual OGRErr ICreateFeature(OGRFeature *poFeature) override;
458 :
459 0 : const char *GetTableName()
460 : {
461 0 : return pszTableName;
462 : }
463 :
464 : const char *GetLayerName()
465 : {
466 : return pszLayerName;
467 : }
468 :
469 0 : const char *GetSchemaName()
470 : {
471 0 : return pszSchemaName;
472 : }
473 :
474 : virtual OGRErr CreateField(const OGRFieldDefn *poField,
475 : int bApproxOK = TRUE) override;
476 :
477 : virtual OGRFeature *GetFeature(GIntBig nFeatureId) override;
478 :
479 : virtual int TestCapability(const char *) override;
480 :
481 0 : void SetLaunderFlag(int bFlag)
482 : {
483 0 : bLaunderColumnNames = bFlag;
484 0 : }
485 :
486 0 : void SetPrecisionFlag(int bFlag)
487 : {
488 0 : bPreservePrecision = bFlag;
489 0 : }
490 :
491 0 : void SetSpatialIndexFlag(int bFlag)
492 : {
493 0 : bNeedSpatialIndex = bFlag;
494 0 : }
495 :
496 0 : void SetUploadGeometryFormat(int nGeometryFormat)
497 : {
498 0 : nUploadGeometryFormat = nGeometryFormat;
499 0 : }
500 :
501 : void AppendFieldValue(CPLODBCStatement *poStatement, OGRFeature *poFeature,
502 : int i, int *bind_num, void **bind_buffer);
503 :
504 : int FetchSRSId();
505 :
506 0 : void SetUseCopy(int bcpSize)
507 : {
508 0 : bUseCopy = TRUE;
509 0 : nBCPSize = bcpSize;
510 0 : }
511 :
512 0 : void SetUpdate(bool bFlag)
513 : {
514 0 : bUpdateAccess = bFlag;
515 0 : }
516 :
517 : static OGRErr StartCopy();
518 :
519 : // cppcheck-suppress functionStatic
520 : OGRErr EndCopy();
521 :
522 : int Failed(int nRetCode);
523 : #ifdef MSSQL_BCP_SUPPORTED
524 : OGRErr CreateFeatureBCP(OGRFeature *poFeature);
525 : int Failed2(int nRetCode);
526 : int InitBCP(const char *pszDSN);
527 : void CloseBCP();
528 : #endif
529 : };
530 :
531 : /************************************************************************/
532 : /* OGRMSSQLSpatialSelectLayer */
533 : /************************************************************************/
534 :
535 : class OGRMSSQLSpatialSelectLayer final : public OGRMSSQLSpatialLayer
536 : {
537 : char *pszBaseStatement;
538 :
539 : virtual CPLODBCStatement *GetStatement() override;
540 :
541 : public:
542 : OGRMSSQLSpatialSelectLayer(OGRMSSQLSpatialDataSource *, CPLODBCStatement *);
543 : virtual ~OGRMSSQLSpatialSelectLayer();
544 :
545 : virtual GIntBig GetFeatureCount(int) override;
546 :
547 : virtual OGRFeature *GetFeature(GIntBig nFeatureId) override;
548 :
549 : virtual int TestCapability(const char *) override;
550 : };
551 :
552 : /************************************************************************/
553 : /* OGRODBCDataSource */
554 : /************************************************************************/
555 :
556 : class OGRMSSQLSpatialDataSource final : public GDALDataset
557 : {
558 : typedef struct
559 : {
560 : int nMajor;
561 : int nMinor;
562 : int nBuild;
563 : int nRevision;
564 : } MSSQLVer;
565 :
566 : OGRMSSQLSpatialTableLayer **papoLayers;
567 : int nLayers;
568 :
569 : char *pszCatalog;
570 :
571 : bool bDSUpdate;
572 : CPLODBCSession oSession;
573 :
574 : int nGeometryFormat;
575 :
576 : int bUseGeometryColumns;
577 : bool bAlwaysOutputFid;
578 :
579 : int bListAllTables;
580 :
581 : int nBCPSize;
582 : int bUseCopy;
583 :
584 : // We maintain a list of known SRID to reduce the number of trips to
585 : // the database to get SRSes.
586 : std::map<int,
587 : std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser>>
588 : m_oSRSCache{};
589 :
590 : OGRMSSQLSpatialTableLayer *poLayerInCopyMode;
591 :
592 : static void OGRMSSQLDecodeVersionString(MSSQLVer *psVersion,
593 : const char *pszVer);
594 :
595 : char *pszConnection;
596 :
597 : public:
598 : MSSQLVer sMSSQLVersion;
599 :
600 : OGRMSSQLSpatialDataSource();
601 : virtual ~OGRMSSQLSpatialDataSource();
602 :
603 0 : const char *GetCatalog()
604 : {
605 0 : return pszCatalog;
606 : }
607 :
608 : static int ParseValue(char **pszValue, char *pszSource, const char *pszKey,
609 : int nStart, int nNext, int nTerm, int bRemove);
610 :
611 : int Open(const char *, bool bUpdate, int bTestOpen);
612 : int OpenTable(const char *pszSchemaName, const char *pszTableName,
613 : const char *pszGeomCol, int nCoordDimension, int nSRID,
614 : const char *pszSRText, OGRwkbGeometryType eType,
615 : bool bUpdate);
616 :
617 : int GetLayerCount() override;
618 : OGRLayer *GetLayer(int) override;
619 : OGRLayer *GetLayerByName(const char *pszLayerName) override;
620 :
621 0 : int GetGeometryFormat()
622 : {
623 0 : return nGeometryFormat;
624 : }
625 :
626 0 : int UseGeometryColumns()
627 : {
628 0 : return bUseGeometryColumns;
629 : }
630 :
631 0 : bool AlwaysOutputFid()
632 : {
633 0 : return bAlwaysOutputFid;
634 : }
635 :
636 : virtual OGRErr DeleteLayer(int iLayer) override;
637 : OGRLayer *ICreateLayer(const char *pszName,
638 : const OGRGeomFieldDefn *poGeomFieldDefn,
639 : CSLConstList papszOptions) override;
640 :
641 : int TestCapability(const char *) override;
642 :
643 : virtual OGRLayer *ExecuteSQL(const char *pszSQLCommand,
644 : OGRGeometry *poSpatialFilter,
645 : const char *pszDialect) override;
646 : virtual void ReleaseResultSet(OGRLayer *poLayer) override;
647 :
648 : static char *LaunderName(const char *pszSrcName);
649 : OGRErr InitializeMetadataTables();
650 :
651 : OGRSpatialReference *AddSRIDToCache(
652 : int nId,
653 : std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser>
654 : &&poSRS);
655 :
656 : OGRSpatialReference *FetchSRS(int nId);
657 : int FetchSRSId(const OGRSpatialReference *poSRS);
658 :
659 : OGRErr StartTransaction(CPL_UNUSED int bForce) override;
660 : OGRErr CommitTransaction() override;
661 : OGRErr RollbackTransaction() override;
662 :
663 : // Internal use
664 0 : CPLODBCSession *GetSession()
665 : {
666 0 : return &oSession;
667 : }
668 :
669 : const char *GetConnectionString()
670 : {
671 : return pszConnection;
672 : }
673 :
674 : void StartCopy(OGRMSSQLSpatialTableLayer *poMSSQLSpatialLayer);
675 : OGRErr EndCopy();
676 : };
677 :
678 : #endif /* ndef OGR_MSSQLSPATIAL_H_INCLUDED */
|