1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Implements OGRPGeoLayer class, code shared between
5  *           the direct table access, and the generic SQL results.
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  ******************************************************************************
9  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
10  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "cpl_conv.h"
32 #include "ogr_pgeo.h"
33 #include "cpl_string.h"
34 #include "ogrpgeogeometry.h"
35 
36 #include <algorithm>
37 
38 CPL_CVSID("$Id: ogrpgeolayer.cpp 86712ccad3229729b777b63858c96d87e1c67b5e 2021-08-23 18:39:33 +0200 Even Rouault $")
39 
40 /************************************************************************/
41 /*                            OGRPGeoLayer()                            */
42 /************************************************************************/
43 
OGRPGeoLayer()44 OGRPGeoLayer::OGRPGeoLayer() :
45     poFeatureDefn(nullptr),
46     poStmt(nullptr),
47     poSRS(nullptr),
48     nSRSId(-2), // we haven't even queried the database for it yet.
49     iNextShapeId(0),
50     poDS(nullptr),
51     pszGeomColumn(nullptr),
52     pszFIDColumn(nullptr),
53     panFieldOrdinals(nullptr)
54 {}
55 
56 /************************************************************************/
57 /*                            ~OGRPGeoLayer()                             */
58 /************************************************************************/
59 
~OGRPGeoLayer()60 OGRPGeoLayer::~OGRPGeoLayer()
61 
62 {
63     if( m_nFeaturesRead > 0 && poFeatureDefn != nullptr )
64     {
65         CPLDebug( "PGeo", "%d features read on layer '%s'.",
66                   static_cast<int>(m_nFeaturesRead),
67                   poFeatureDefn->GetName() );
68     }
69 
70     if( poStmt != nullptr )
71     {
72         delete poStmt;
73         poStmt = nullptr;
74     }
75 
76     if( poFeatureDefn != nullptr )
77     {
78         poFeatureDefn->Release();
79         poFeatureDefn = nullptr;
80     }
81 
82     CPLFree( pszGeomColumn );
83     CPLFree( panFieldOrdinals );
84     CPLFree( pszFIDColumn );
85 
86     if( poSRS != nullptr )
87     {
88         poSRS->Release();
89         poSRS = nullptr;
90     }
91 }
92 
93 /************************************************************************/
94 /*                          BuildFeatureDefn()                          */
95 /*                                                                      */
96 /*      Build feature definition from a set of column definitions       */
97 /*      set on a statement.  Sift out geometry and FID fields.          */
98 /************************************************************************/
99 
BuildFeatureDefn(const char * pszLayerName,CPLODBCStatement * poStmtIn)100 CPLErr OGRPGeoLayer::BuildFeatureDefn( const char *pszLayerName,
101                                        CPLODBCStatement *poStmtIn )
102 
103 {
104     poFeatureDefn = new OGRFeatureDefn( pszLayerName );
105     SetDescription( poFeatureDefn->GetName() );
106     int    nRawColumns = poStmtIn->GetColCount();
107 
108     poFeatureDefn->Reference();
109     poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
110 
111     panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * nRawColumns );
112 
113     for( int iCol = 0; iCol < nRawColumns; iCol++ )
114     {
115         OGRFieldDefn    oField( poStmtIn->GetColName(iCol), OFTString );
116 
117         oField.SetWidth(
118             std::max(static_cast<short>(0), poStmtIn->GetColSize( iCol )));
119 
120         if( pszGeomColumn != nullptr
121             && EQUAL(poStmtIn->GetColName(iCol),pszGeomColumn) )
122             continue;
123 
124         if( pszFIDColumn == nullptr
125             && EQUAL(poStmtIn->GetColName(iCol),"OBJECTID") )
126         {
127             pszFIDColumn = CPLStrdup(poStmtIn->GetColName(iCol));
128         }
129 
130         if( pszGeomColumn == nullptr
131             && EQUAL(poStmtIn->GetColName(iCol),"Shape") )
132         {
133             pszGeomColumn = CPLStrdup(poStmtIn->GetColName(iCol));
134             continue;
135         }
136 
137         switch( poStmtIn->GetColType(iCol) )
138         {
139           case SQL_INTEGER:
140           case SQL_SMALLINT:
141             oField.SetType( OFTInteger );
142             break;
143 
144           case SQL_BINARY:
145           case SQL_VARBINARY:
146           case SQL_LONGVARBINARY:
147             oField.SetType( OFTBinary );
148             break;
149 
150           case SQL_DECIMAL:
151             oField.SetType( OFTReal );
152             oField.SetPrecision( poStmtIn->GetColPrecision(iCol) );
153             break;
154 
155           case SQL_FLOAT:
156           case SQL_REAL:
157           case SQL_DOUBLE:
158             oField.SetType( OFTReal );
159             oField.SetWidth( 0 );
160             break;
161 
162           case SQL_C_DATE:
163             oField.SetType( OFTDate );
164             break;
165 
166           case SQL_C_TIME:
167             oField.SetType( OFTTime );
168             break;
169 
170           case SQL_C_TIMESTAMP:
171           case SQL_C_TYPE_TIMESTAMP:
172             oField.SetType( OFTDateTime );
173             break;
174 
175           default:
176             /* leave it as OFTString */;
177         }
178 
179         poFeatureDefn->AddFieldDefn( &oField );
180         panFieldOrdinals[poFeatureDefn->GetFieldCount() - 1] = iCol+1;
181     }
182 
183     if( pszGeomColumn != nullptr )
184         poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColumn);
185     else
186         poFeatureDefn->SetGeomType( wkbNone );
187 
188     return CE_None;
189 }
190 
191 /************************************************************************/
192 /*                            ResetReading()                            */
193 /************************************************************************/
194 
ResetReading()195 void OGRPGeoLayer::ResetReading()
196 
197 {
198     iNextShapeId = 0;
199     m_bEOF = false;
200 }
201 
202 /************************************************************************/
203 /*                           GetNextFeature()                           */
204 /************************************************************************/
205 
GetNextFeature()206 OGRFeature *OGRPGeoLayer::GetNextFeature()
207 
208 {
209     while( true )
210     {
211         OGRFeature *poFeature = GetNextRawFeature();
212         if( poFeature == nullptr )
213             return nullptr;
214 
215         if( (m_poFilterGeom == nullptr
216             || FilterGeometry( poFeature->GetGeometryRef() ) )
217             && (m_poAttrQuery == nullptr
218                 || m_poAttrQuery->Evaluate( poFeature )) )
219             return poFeature;
220 
221         delete poFeature;
222     }
223 }
224 
225 /************************************************************************/
226 /*                         GetNextRawFeature()                          */
227 /************************************************************************/
228 
GetNextRawFeature()229 OGRFeature *OGRPGeoLayer::GetNextRawFeature()
230 
231 {
232     OGRErr err = OGRERR_NONE;
233 
234     if( m_bEOF || GetStatement() == nullptr )
235         return nullptr;
236 
237 /* -------------------------------------------------------------------- */
238 /*      If we are marked to restart then do so, and fetch a record.     */
239 /* -------------------------------------------------------------------- */
240     if( !poStmt->Fetch() )
241     {
242         delete poStmt;
243         poStmt = nullptr;
244         m_bEOF = true;
245         return nullptr;
246     }
247 
248 /* -------------------------------------------------------------------- */
249 /*      Create a feature from the current result.                       */
250 /* -------------------------------------------------------------------- */
251     OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
252 
253     if( pszFIDColumn != nullptr && poStmt->GetColId(pszFIDColumn) > -1 )
254         poFeature->SetFID(
255             atoi(poStmt->GetColData(poStmt->GetColId(pszFIDColumn))) );
256     else
257         poFeature->SetFID( iNextShapeId );
258 
259     iNextShapeId++;
260     m_nFeaturesRead++;
261 
262 /* -------------------------------------------------------------------- */
263 /*      Set the fields.                                                 */
264 /* -------------------------------------------------------------------- */
265     for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
266     {
267         int iSrcField = panFieldOrdinals[iField]-1;
268         const char *pszValue = poStmt->GetColData( iSrcField );
269 
270         if( pszValue == nullptr )
271             poFeature->SetFieldNull( iField );
272         else if( poFeature->GetFieldDefnRef(iField)->GetType() == OFTBinary )
273             poFeature->SetField( iField,
274                                  poStmt->GetColDataLength(iSrcField),
275                                  (GByte *) pszValue );
276         else
277             poFeature->SetField( iField, pszValue );
278     }
279 
280 /* -------------------------------------------------------------------- */
281 /*      Try to extract a geometry.                                      */
282 /* -------------------------------------------------------------------- */
283     if( pszGeomColumn != nullptr )
284     {
285         int iField = poStmt->GetColId( pszGeomColumn );
286         GByte *pabyShape = (GByte *) poStmt->GetColData( iField );
287         int nBytes = poStmt->GetColDataLength(iField);
288         OGRGeometry *poGeom = nullptr;
289 
290         if( pabyShape != nullptr )
291         {
292             err = OGRCreateFromShapeBin( pabyShape, &poGeom, nBytes );
293             if( OGRERR_NONE != err )
294             {
295                 CPLDebug( "PGeo",
296                           "Translation shape binary to OGR geometry failed (FID=%ld)",
297                            (long)poFeature->GetFID() );
298             }
299         }
300 
301         if( poGeom != nullptr && OGRERR_NONE == err )
302         {
303             poGeom->assignSpatialReference( poSRS );
304             poFeature->SetGeometryDirectly( poGeom );
305         }
306     }
307 
308     return poFeature;
309 }
310 
311 /************************************************************************/
312 /*                             GetFeature()                             */
313 /************************************************************************/
314 
GetFeature(GIntBig nFeatureId)315 OGRFeature *OGRPGeoLayer::GetFeature( GIntBig nFeatureId )
316 
317 {
318     /* This should be implemented directly! */
319 
320     return OGRLayer::GetFeature( nFeatureId );
321 }
322 
323 /************************************************************************/
324 /*                           TestCapability()                           */
325 /************************************************************************/
326 
TestCapability(CPL_UNUSED const char * pszCap)327 int OGRPGeoLayer::TestCapability( CPL_UNUSED const char * pszCap )
328 {
329     return FALSE;
330 }
331 
332 /************************************************************************/
333 /*                           TestCapability()                           */
334 /************************************************************************/
335 
LookupSRID(int nSRID)336 void OGRPGeoLayer::LookupSRID( int nSRID )
337 
338 {
339 /* -------------------------------------------------------------------- */
340 /*      Fetch the corresponding WKT from the SpatialRef table.          */
341 /* -------------------------------------------------------------------- */
342     CPLODBCStatement oStmt( poDS->GetSession() );
343 
344     oStmt.Appendf( "SELECT srtext FROM GDB_SpatialRefs WHERE srid = %d",
345                   nSRID );
346 
347     if( !oStmt.ExecuteSQL() )
348     {
349         CPLError( CE_Failure, CPLE_AppDefined,
350                   "'%s' failed.\n%s",
351                   oStmt.GetCommand(),
352                   poDS->GetSession()->GetLastError() );
353         return;
354     }
355 
356     if( !oStmt.Fetch() )
357     {
358         CPLError( CE_Warning, CPLE_AppDefined,
359                   "SRID %d lookup failed.\n%s",
360                   nSRID, poDS->GetSession()->GetLastError() );
361         return;
362     }
363 
364 /* -------------------------------------------------------------------- */
365 /*      Check that it isn't just a GUID.  We don't know how to          */
366 /*      translate those.                                                */
367 /* -------------------------------------------------------------------- */
368     const char *pszSRText = oStmt.GetColData(0);
369 
370     if( pszSRText[0] == '{' )
371     {
372         CPLDebug( "PGEO", "Ignoring GUID SRTEXT: %s", pszSRText );
373         return;
374     }
375 
376 /* -------------------------------------------------------------------- */
377 /*      Turn it into an OGRSpatialReference.                            */
378 /* -------------------------------------------------------------------- */
379     poSRS = new OGRSpatialReference();
380     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
381 
382     if( poSRS->importFromWkt( pszSRText ) != OGRERR_NONE )
383     {
384         CPLError( CE_Failure, CPLE_AppDefined,
385                   "importFromWKT() failed on SRS '%s'.",
386                   pszSRText);
387         delete poSRS;
388         poSRS = nullptr;
389     }
390     else
391         nSRSId = nSRID;
392 }
393 
394 /************************************************************************/
395 /*                            GetFIDColumn()                            */
396 /************************************************************************/
397 
GetFIDColumn()398 const char *OGRPGeoLayer::GetFIDColumn()
399 
400 {
401     if( pszFIDColumn != nullptr )
402         return pszFIDColumn;
403     else
404         return "";
405 }
406 
407 /************************************************************************/
408 /*                         GetGeometryColumn()                          */
409 /************************************************************************/
410 
GetGeometryColumn()411 const char *OGRPGeoLayer::GetGeometryColumn()
412 
413 {
414     if( pszGeomColumn != nullptr )
415         return pszGeomColumn;
416     else
417         return "";
418 }
419