1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Implements OGRIDBLayer class, code shared between
5  *           the direct table access, and the generic SQL results
6  *           (based on ODBC and PG drivers).
7  * Author:   Oleg Semykin, oleg.semykin@gmail.com
8  *
9  ******************************************************************************
10  * Copyright (c) 2006, Oleg Semykin
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_idb.h"
33 #include "cpl_string.h"
34 
35 CPL_CVSID("$Id: ogridblayer.cpp 417b66cdf50174fe8d59833c93710813f205d9ba 2020-08-30 12:18:19 +0200 Even Rouault $")
36 
37 /************************************************************************/
38 /*                            OGRIDBLayer()                            */
~OGRIDBDriver()39 /************************************************************************/
40 
41 OGRIDBLayer::OGRIDBLayer()
42 
43 {
44     poDS = nullptr;
45 
46     bGeomColumnWKB = FALSE;
47     pszFIDColumn = nullptr;
48     pszGeomColumn = nullptr;
49 
50     m_poCurr = nullptr;
51 
52     iNextShapeId = 0;
53 
54     poSRS = nullptr;
55     nSRSId = -2; // we haven't even queried the database for it yet.
56     poFeatureDefn = nullptr;
57 }
Open(const char * pszFilename,int bUpdate)58 
59 /************************************************************************/
60 /*                            ~OGRIDBLayer()                             */
61 /************************************************************************/
62 
63 OGRIDBLayer::~OGRIDBLayer()
64 
65 {
66     if( m_poCurr != nullptr )
67     {
68         m_poCurr->Close();
69         delete m_poCurr;
70         m_poCurr = nullptr;
71     }
72 
73     if( pszGeomColumn )
74         CPLFree( pszGeomColumn );
75 
76     if(pszFIDColumn)
77         CPLFree( pszFIDColumn );
78 
79     if( poFeatureDefn )
80     {
81         poFeatureDefn->Release();
82         poFeatureDefn = nullptr;
83     }
84 
85     if( poSRS )
86         poSRS->Release();
87 }
88 
89 /************************************************************************/
90 /*                          BuildFeatureDefn()                          */
91 /*                                                                      */
92 /*      Build feature definition from a set of column definitions       */
93 /*      set on a statement.  Sift out geometry and FID fields.          */
94 /************************************************************************/
95 
96 CPLErr OGRIDBLayer::BuildFeatureDefn( const char *pszLayerName,
97                                     ITCursor *poCurr )
98 
99 {
100     poFeatureDefn = new OGRFeatureDefn( pszLayerName );
101     SetDescription( poFeatureDefn->GetName() );
102     const ITTypeInfo * poInfo = poCurr->RowType();
103     int    nRawColumns = poInfo->ColumnCount();
104 
105     poFeatureDefn->Reference();
106 
107     for( int iCol = 0; iCol < nRawColumns; iCol++ )
TestCapability(const char * pszCap)108     {
109         const char * pszColName = poInfo->ColumnName(iCol);
110         const ITTypeInfo * poTI = poInfo->ColumnType(iCol);
111         const char * pszTypName = poTI->Name();
112 
113         OGRFieldDefn    oField( pszColName, OFTString );
114 
115         oField.SetWidth( MAX(0,poTI->Bound()) );
116 
117         if ( pszGeomColumn != nullptr && EQUAL(pszColName,pszGeomColumn) )
118             continue;
119 
120         if ( STARTS_WITH_CI(pszTypName, "st_") && pszGeomColumn == nullptr )
121         {
122             // We found spatial column!
123             pszGeomColumn = CPLStrdup(pszColName);
124 
125             if ( EQUAL("st_point", pszTypName) )
126                 poFeatureDefn->SetGeomType( wkbPoint );
127             else if ( EQUAL("st_linestring", pszTypName) )
128                 poFeatureDefn->SetGeomType( wkbLineString );
129             else if ( EQUAL("st_polygon", pszTypName) )
130                 poFeatureDefn->SetGeomType( wkbPolygon );
131             else if ( EQUAL("st_multipoint", pszTypName) )
132                 poFeatureDefn->SetGeomType( wkbMultiPoint );
133             else if ( EQUAL("st_multilinestring", pszTypName) )
134                 poFeatureDefn->SetGeomType( wkbMultiLineString );
135             else if ( EQUAL("st_multipolygon", pszTypName) )
136                 poFeatureDefn->SetGeomType( wkbMultiPolygon );
137 
138             continue;
139         }
140 
141         // Check other field types
142         if ( EQUAL( pszTypName, "blob" ) ||
143              EQUAL( pszTypName, "byte" ) ||
144              EQUAL( pszTypName, "opaque" ) ||
145              EQUAL( pszTypName, "text" ) ||
146              STARTS_WITH_CI(pszTypName, "list") ||
147              STARTS_WITH_CI(pszTypName, "collection") ||
148              STARTS_WITH_CI(pszTypName, "row") ||
149              STARTS_WITH_CI(pszTypName, "set") )
150         {
151             CPLDebug( "OGR_IDB", "'%s' column type not supported yet. Column '%s'",
152                       pszTypName, pszColName );
153             continue;
154         }
155 
156         if ( STARTS_WITH_CI(pszTypName, "st_") )
157         {
158             oField.SetType( OFTBinary );
159         }
160         else if ( EQUAL( pszTypName, "date" ) )
161         {
162             oField.SetType( OFTDate );
163         }
164         else if ( EQUAL( pszTypName, "datetime" ) )
165         {
166             oField.SetType( OFTDateTime );
167         }
168         else if ( EQUAL( pszTypName, "decimal" ) ||
169                   EQUAL( pszTypName, "money" ) ||
170                   EQUAL( pszTypName, "float" ) ||
171                   EQUAL( pszTypName, "smallfloat" ) )
172         {
173             oField.SetType( OFTReal );
174             oField.SetPrecision( MAX( 0, poTI->Scale() ) ); // -1 for numeric
175         }
176         else if ( EQUAL( pszTypName, "integer" ) ||
177                   EQUAL( pszTypName, "serial" ) )
178         {
179             oField.SetType( OFTInteger );
180             // 10 as hardcoded max int32 value length + 1 sig bit
181             oField.SetWidth( 11 );
182         }
183         else if ( EQUAL( pszTypName, "smallint" ) )
184         {
185             oField.SetType( OFTInteger );
186             // 5 as hardcoded max int16 value length + 1 sig bit
187             oField.SetWidth( 6 );
188         }
189         else
190         {
191             // leave as string:
192             // *char, character, character varying, *varchar
193             // interval. int8, serial8
194         }
195 
196         poFeatureDefn->AddFieldDefn( &oField );
197     }
198 
199 /* -------------------------------------------------------------------- */
200 /*      If we don't already have an FID, check if there is a special    */
201 /*      FID named column available.                                     */
202 /* -------------------------------------------------------------------- */
203     if( pszFIDColumn == nullptr )
204     {
205         const char *pszOGR_FID = CPLGetConfigOption("IDB_OGR_FID","OGR_FID");
206         if( poFeatureDefn->GetFieldIndex( pszOGR_FID ) != -1 )
207             pszFIDColumn = CPLStrdup(pszOGR_FID);
208     }
209 
210     if( pszFIDColumn != nullptr )
211         CPLDebug( "OGR_IDB", "Using column %s as FID for table %s.",
212                   pszFIDColumn, poFeatureDefn->GetName() );
213     else
214         CPLDebug( "OGR_IDB", "Table %s has no identified FID column.",
215                   poFeatureDefn->GetName() );
216 
217     return CE_None;
218 }
219 
220 /************************************************************************/
221 /*                            ResetReading()                            */
222 /************************************************************************/
223 
224 void OGRIDBLayer::ResetReading()
225 
226 {
227     iNextShapeId = 0;
228 }
229 
230 /************************************************************************/
231 /*                           GetNextFeature()                           */
232 /************************************************************************/
233 
234 OGRFeature *OGRIDBLayer::GetNextFeature()
235 
236 {
237     while( true )
238     {
239         OGRFeature      *poFeature;
240 
241         poFeature = GetNextRawFeature();
242         if( poFeature == nullptr )
243             return nullptr;
244 
245         if( (m_poFilterGeom == nullptr
246             || FilterGeometry( poFeature->GetGeometryRef() ) )
247             && (m_poAttrQuery == nullptr
248                 || m_poAttrQuery->Evaluate( poFeature )) )
249             return poFeature;
250 
251         delete poFeature;
252     }
253 }
254 
255 /************************************************************************/
256 /*                         GetNextRawFeature()                          */
257 /************************************************************************/
258 
259 OGRFeature *OGRIDBLayer::GetNextRawFeature()
260 
261 {
262     if( GetQuery() == nullptr )
263         return nullptr;
264 
265 /* -------------------------------------------------------------------- */
266 /*      If we are marked to restart then do so, and fetch a record.     */
267 /* -------------------------------------------------------------------- */
268     ITRow * row = m_poCurr->NextRow();
269     if ( ! row )
270     {
271         delete m_poCurr;
272         m_poCurr = nullptr;
273         return nullptr;
274     }
275 
276     iNextShapeId++;
277     m_nFeaturesRead++;
278 
279 /* -------------------------------------------------------------------- */
280 /*      Create a feature from the current result.                       */
281 /* -------------------------------------------------------------------- */
282     int         iField;
283     OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
284 
285     const ITTypeInfo * poRowType = m_poCurr->RowType();
286     int nFieldCount = poRowType->ColumnCount();
287 
288     for ( iField = 0; iField < nFieldCount; iField++ )
289     {
290 /* -------------------------------------------------------------------- */
291 /*      Handle FID column                                               */
292 /* -------------------------------------------------------------------- */
293         if ( pszFIDColumn != nullptr &&
294              EQUAL( poRowType->ColumnName( iField ), pszFIDColumn ) )
295             poFeature->SetFID( atoi( row->Column( iField )->Printable() ) );
296 
297 /* -------------------------------------------------------------------- */
298 /*      Handle geometry                                                 */
299 /* -------------------------------------------------------------------- */
300         if( pszGeomColumn != nullptr &&
301             EQUAL( poRowType->ColumnName( iField ), pszGeomColumn ) )
302         {
303             OGRGeometry *poGeom = nullptr;
304             OGRErr eErr = OGRERR_NONE;
305 
306             ITValue * v = row->Column( iField );
307 
308             if( ! v->IsNull() && ! bGeomColumnWKB )
309             {
310                 const char *pszGeomText = v->Printable();
311                 if ( pszGeomText != nullptr )
312                 eErr =
313                     OGRGeometryFactory::createFromWkt(pszGeomText,
314                                                     poSRS, &poGeom);
315             }
316             else if( ! v->IsNull() && bGeomColumnWKB )
317             {
318                 ITDatum *rv = nullptr;
319                 if ( v->QueryInterface( ITDatumIID, (void **) &rv ) ==
320                      IT_QUERYINTERFACE_SUCCESS )
321                 {
322                     int nLength = rv->DataLength();
323                     unsigned char * wkb = (unsigned char *)rv->Data();
324 
325                     eErr = OGRGeometryFactory::createFromWkb( wkb, poSRS, &poGeom, nLength);
326                     rv->Release();
327                 }
328             }
329 
330             v->Release();
331 
332             if ( eErr != OGRERR_NONE )
333             {
334                 const char *pszMessage;
335 
336                 switch ( eErr )
337                 {
338                     case OGRERR_NOT_ENOUGH_DATA:
339                         pszMessage = "Not enough data to deserialize";
340                         break;
341                     case OGRERR_UNSUPPORTED_GEOMETRY_TYPE:
342                         pszMessage = "Unsupported geometry type";
343                         break;
344                     case OGRERR_CORRUPT_DATA:
345                         pszMessage = "Corrupt data";
346                         break;
347                     default:
348                         pszMessage = "Unrecognized error";
349                 }
350                 CPLError(CE_Failure, CPLE_AppDefined,
351                         "GetNextRawFeature(): %s", pszMessage);
352             }
353 
354             if( poGeom != nullptr )
355             {
356                 poFeature->SetGeometryDirectly( poGeom );
357             }
358 
359             continue;
360         }
361 
362 /* -------------------------------------------------------------------- */
363 /*      Transfer regular data fields.                                   */
364 /* -------------------------------------------------------------------- */
365         int iOGRField =
366             poFeatureDefn->GetFieldIndex( poRowType->ColumnName( iField ) );
367 
368         if( iOGRField < 0 )
369             continue;
370 
371         const char * pszColData = row->Column( iField )->Printable();
372 
373         if( ! pszColData  )
374             continue;
375 
376         if( poFeatureDefn->GetFieldDefn(iOGRField)->GetType() == OFTBinary )
377             poFeature->SetField( iOGRField,
378                                  poRowType->ColumnType( iField )->Size(),
379                                  (GByte *) pszColData );
380         else
381             poFeature->SetField( iOGRField, pszColData );
382     }
383 
384     row->Release();
385     return poFeature;
386 }
387 
388 /************************************************************************/
389 /*                             GetFeature()                             */
390 /************************************************************************/
391 
392 OGRFeature *OGRIDBLayer::GetFeature( GIntBig nFeatureId )
393 
394 {
395     /* This should be implemented directly! */
396 
397     return OGRLayer::GetFeature( nFeatureId );
398 }
399 
400 /************************************************************************/
401 /*                           TestCapability()                           */
402 /************************************************************************/
403 
404 int OGRIDBLayer::TestCapability( const char * /*pszCap*/ )
405 
406 {
407     return FALSE;
408 }
409 
410 /************************************************************************/
411 /*                           GetSpatialRef()                            */
412 /************************************************************************/
413 
414 OGRSpatialReference *OGRIDBLayer::GetSpatialRef()
415 
416 {
417     return poSRS;
418 }
419 
420 /************************************************************************/
421 /*                            GetFIDColumn()                            */
422 /************************************************************************/
423 
424 const char *OGRIDBLayer::GetFIDColumn()
425 
426 {
427     if( pszFIDColumn != nullptr )
428         return pszFIDColumn;
429     else
430         return "";
431 }
432 
433 /************************************************************************/
434 /*                         GetGeometryColumn()                          */
435 /************************************************************************/
436 
437 const char *OGRIDBLayer::GetGeometryColumn()
438 
439 {
440     if( pszGeomColumn != nullptr )
441         return pszGeomColumn;
442     else
443         return "";
444 }
445 
446 /* TODO Query to get layer extent */
447 /*
448 EXECUTE FUNCTION SE_BoundingBox ('table_name', 'geom_column' )
449 */
450