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