1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Implements OGRIDBTableLayer class, access to an existing table
5  *           (based on ODBC and PG drivers).
6  * Author:   Oleg Semykin, oleg.semykin@gmail.com
7  *
8  ******************************************************************************
9  * Copyright (c) 2006, Oleg Semykin
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_conv.h"
31 #include "cpl_string.h"
32 #include "ogr_idb.h"
33 
34 CPL_CVSID("$Id: ogridbtablelayer.cpp 417b66cdf50174fe8d59833c93710813f205d9ba 2020-08-30 12:18:19 +0200 Even Rouault $")
35 /************************************************************************/
36 /*                          OGRIDBTableLayer()                         */
37 /************************************************************************/
38 
OGRIDBTableLayer(OGRIDBDataSource * poDSIn)39 OGRIDBTableLayer::OGRIDBTableLayer( OGRIDBDataSource *poDSIn )
40 
41 {
42     poDS = poDSIn;
43 
44     pszQuery = nullptr;
45 
46     bUpdateAccess = TRUE;
47     bHaveSpatialExtents = FALSE;
48 
49     iNextShapeId = 0;
50 
51     poFeatureDefn = nullptr;
52 }
53 
54 /************************************************************************/
55 /*                          ~OGRIDBTableLayer()                          */
56 /************************************************************************/
57 
~OGRIDBTableLayer()58 OGRIDBTableLayer::~OGRIDBTableLayer()
59 
60 {
61     CPLFree( pszQuery );
62     ClearQuery();
63 }
64 
65 /************************************************************************/
66 /*                             Initialize()                             */
67 /************************************************************************/
68 
Initialize(const char * pszTableName,const char * pszGeomCol,int bUpdate)69 CPLErr OGRIDBTableLayer::Initialize( const char *pszTableName,
70                                      const char *pszGeomCol,
71                                      int bUpdate )
72 
73 {
74     bUpdateAccess = bUpdate;
75 
76     ITConnection *poConn = poDS->GetConnection();
77 
78     if ( pszFIDColumn )
79     {
80         CPLFree( pszFIDColumn );
81         pszFIDColumn = nullptr;
82     }
83 
84 /* -------------------------------------------------------------------- */
85 /*      Do we have a simple primary key?                                */
86 /* -------------------------------------------------------------------- */
87     if ( pszFIDColumn== nullptr )
88     {
89         ITCursor oGetKey( *poConn );
90 
91         CPLString osSql =
92                 " select sc.colname"
93                 " from syscolumns sc, sysindexes si, systables st"
94                 " where st.tabid = si.tabid"
95                 " and st.tabid = sc.tabid"
96                 " and si.idxtype = 'U'"
97                 " and sc.colno = si.part1"
98                 " and si.part2 = 0" // only one-column keys
99                 " and st.tabname='";
100         osSql += pszTableName;
101         osSql += "'";
102 
103         if( oGetKey.Prepare( osSql.c_str() ) &&
104             oGetKey.Open(ITCursor::ReadOnly) )
105         {
106             ITValue * poVal = oGetKey.Fetch();
107             if ( poVal && poVal->IsNull() == false )
108             {
109                 pszFIDColumn = CPLStrdup(poVal->Printable());
110                 poVal->Release();
111             }
112 
113             if( oGetKey.Fetch() ) // more than one field in key!
114             {
115                 CPLFree( pszFIDColumn );
116                 pszFIDColumn = nullptr;
117 
118                 CPLDebug("OGR_IDB", "Table %s has multiple primary key fields,"
119                          " ignoring them all.", pszTableName );
120             }
121         }
122     }
123 
124 /* -------------------------------------------------------------------- */
125 /*      Have we been provided a geometry column?                        */
126 /* -------------------------------------------------------------------- */
127     CPLFree( pszGeomColumn );
128     if( pszGeomCol== nullptr )
129         pszGeomColumn = nullptr;
130     else
131         pszGeomColumn = CPLStrdup( pszGeomCol );
132 
133 /* -------------------------------------------------------------------- */
134 /*      Get the column definitions for this table.                      */
135 /* -------------------------------------------------------------------- */
136     ITCursor oGetCol( *poConn );
137     CPLErr eErr;
138 
139     CPLString sql;
140     sql.Printf( "select * from %s where 1=0", pszTableName );
141     if( ! oGetCol.Prepare( sql.c_str() ) ||
142         ! oGetCol.Open(ITCursor::ReadOnly) )
143         return CE_Failure;
144 
145     eErr = BuildFeatureDefn( pszTableName, &oGetCol );
146     if( eErr != CE_None )
147         return eErr;
148 
149     if( poFeatureDefn->GetFieldCount() == 0 )
150     {
151         CPLError( CE_Failure, CPLE_AppDefined,
152                   "No column definitions found for table '%s', layer not usable.",
153                   pszTableName );
154         return CE_Failure;
155     }
156 
157 /* -------------------------------------------------------------------- */
158 /*      Do we have XMIN, YMIN, XMAX, YMAX extent fields?                */
159 /* -------------------------------------------------------------------- */
160     if( poFeatureDefn->GetFieldIndex( "XMIN" ) != -1
161         && poFeatureDefn->GetFieldIndex( "XMAX" ) != -1
162         && poFeatureDefn->GetFieldIndex( "YMIN" ) != -1
163         && poFeatureDefn->GetFieldIndex( "YMAX" ) != -1 )
164     {
165         bHaveSpatialExtents = TRUE;
166         CPLDebug( "OGR_IDB", "Table %s has geometry extent fields.",
167                   pszTableName );
168     }
169 
170 /* -------------------------------------------------------------------- */
171 /*      If we got a geometry column, does it exist?  Is it binary?      */
172 /* -------------------------------------------------------------------- */
173     if( pszGeomColumn != nullptr )
174     {
175         int iColumn = oGetCol.RowType()->ColumnId( pszGeomColumn );
176         if( iColumn < 0 )
177         {
178             CPLError( CE_Failure, CPLE_AppDefined,
179                       "Column %s requested for geometry, but it does not exist.",
180                       pszGeomColumn );
181             CPLFree( pszGeomColumn );
182             pszGeomColumn = nullptr;
183         }
184         bGeomColumnWKB = TRUE;
185         /*else
186         {
187             if( ITCursor::GetTypeMapping(
188                     oGetCol.GetColType( iColumn )) == SQL_C_BINARY )
189                 bGeomColumnWKB = TRUE;
190         }*/
191     }
192 
193     return CE_None;
194 }
195 
196 /************************************************************************/
197 /*                           ClearQuery()                           */
198 /************************************************************************/
199 
ClearQuery()200 void OGRIDBTableLayer::ClearQuery()
201 
202 {
203     if( m_poCurr != nullptr )
204     {
205         m_poCurr->Close();
206         delete m_poCurr;
207         m_poCurr = nullptr;
208     }
209 }
210 
211 /************************************************************************/
212 /*                            GetQuery()                            */
213 /************************************************************************/
214 
GetQuery()215 ITCursor *OGRIDBTableLayer::GetQuery()
216 
217 {
218     if( m_poCurr== nullptr )
219         ResetQuery();
220 
221     return m_poCurr;
222 }
223 
224 /************************************************************************/
225 /*                           ResetQuery()                           */
226 /************************************************************************/
227 
ResetQuery()228 OGRErr OGRIDBTableLayer::ResetQuery()
229 
230 {
231     ClearQuery();
232 
233     iNextShapeId = 0;
234 
235     m_poCurr = new ITCursor( *poDS->GetConnection() );
236 
237     // Create list of fields
238     CPLString osFields;
239 
240     if ( pszGeomColumn )
241     {
242         if ( ! osFields.empty() )
243             osFields += ",";
244 
245         osFields += "st_asbinary(";
246         osFields += pszGeomColumn;
247         osFields += ") as ";
248         osFields += pszGeomColumn;
249     }
250 
251     for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
252     {
253         if ( ! osFields.empty() )
254             osFields += ",";
255 
256         osFields += poFeatureDefn->GetFieldDefn(i)->GetNameRef();
257     }
258 
259     CPLString sql;
260 
261     sql += "SELECT ";
262     sql += osFields;
263     sql += " FROM ";
264     sql += poFeatureDefn->GetName();
265 
266     /* Append attribute query if we have it */
267     if( pszQuery != nullptr )
268     {
269         sql += " WHERE ";
270         sql += pszQuery;
271     }
272 
273     /* If we have a spatial filter, and per record extents, query on it */
274     if( m_poFilterGeom != nullptr && bHaveSpatialExtents )
275     {
276         if( pszQuery== nullptr )
277             sql += " WHERE";
278         else
279             sql += " AND";
280 
281         CPLString sqlTmp;
282         sqlTmp.Printf( "%s XMAX > %.8f AND XMIN < %.8f"
283                     " AND YMAX > %.8f AND YMIN < %.8f",
284                     sql.c_str(),
285                     m_sFilterEnvelope.MinX, m_sFilterEnvelope.MaxX,
286                     m_sFilterEnvelope.MinY, m_sFilterEnvelope.MaxY );
287         sql = sqlTmp;
288     }
289     /* If we have a spatial filter and GeomColumn, query using st_intersects function */
290     else if( m_poFilterGeom != nullptr && pszGeomColumn )
291     {
292         if( pszQuery== nullptr )
293             sql += " WHERE";
294         else
295             sql += " AND";
296 
297         CPLString sqlTmp;
298         sqlTmp.Printf(
299                 "%s st_intersects(st_geomfromtext('POLYGON((%.8f %.8f, %.8f %.8f, %.8f %.8f, %.8f %.8f, %.8f %.8f))',0),%s)",
300                 sql.c_str(),
301                 m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY,
302                 m_sFilterEnvelope.MaxX, m_sFilterEnvelope.MinY,
303                 m_sFilterEnvelope.MaxX, m_sFilterEnvelope.MaxY,
304                 m_sFilterEnvelope.MinX, m_sFilterEnvelope.MaxY,
305                 m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY, pszGeomColumn );
306         sql = sqlTmp;
307     }
308 
309     CPLDebug( "OGR_IDB", "Exec(%s)", sql.c_str() );
310     if( m_poCurr->Prepare( sql.c_str() ) &&
311         m_poCurr->Open(ITCursor::ReadOnly) )
312     {
313         return OGRERR_NONE;
314     }
315     else
316     {
317         delete m_poCurr;
318         m_poCurr = nullptr;
319         return OGRERR_FAILURE;
320     }
321 }
322 
323 /************************************************************************/
324 /*                            ResetReading()                            */
325 /************************************************************************/
326 
ResetReading()327 void OGRIDBTableLayer::ResetReading()
328 
329 {
330     ClearQuery();
331     OGRIDBLayer::ResetReading();
332 }
333 
334 /************************************************************************/
335 /*                             GetFeature()                             */
336 /************************************************************************/
337 
GetFeature(GIntBig nFeatureId)338 OGRFeature *OGRIDBTableLayer::GetFeature( GIntBig nFeatureId )
339 
340 {
341     if( pszFIDColumn== nullptr )
342         return OGRIDBLayer::GetFeature( nFeatureId );
343 
344     ClearQuery();
345 
346     iNextShapeId = nFeatureId;
347 
348     m_poCurr = new ITCursor( *poDS->GetConnection() );
349 
350     // Create list of fields
351     CPLString osFields;
352 
353     if ( poFeatureDefn->GetFieldIndex( pszFIDColumn ) == -1 )
354         osFields += pszFIDColumn;
355 
356     if ( pszGeomColumn )
357     {
358         if ( ! osFields.empty() )
359             osFields += ",";
360 
361         osFields += "st_asbinary(";
362         osFields += pszGeomColumn;
363         osFields += ") as ";
364         osFields += pszGeomColumn;
365     }
366 
367     for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
368     {
369         if ( ! osFields.empty() )
370             osFields += ",";
371 
372         osFields += poFeatureDefn->GetFieldDefn(i)->GetNameRef();
373     }
374 
375     CPLString sql;
376 
377     sql.Printf( "SELECT %s FROM %s WHERE %s = " CPL_FRMT_GIB,
378                 osFields.c_str(), poFeatureDefn->GetName(),
379                 pszFIDColumn, nFeatureId );
380 
381     CPLDebug( "OGR_IDB", "ExecuteSQL(%s)", sql.c_str() );
382     if( !m_poCurr->Prepare( sql.c_str() ) ||
383         !m_poCurr->Open(ITCursor::ReadOnly) )
384     {
385         delete m_poCurr;
386         m_poCurr = nullptr;
387         return nullptr;
388     }
389 
390     return GetNextRawFeature();
391 }
392 
393 /************************************************************************/
394 /*                         SetAttributeFilter()                         */
395 /************************************************************************/
396 
SetAttributeFilter(const char * pszQueryIn)397 OGRErr OGRIDBTableLayer::SetAttributeFilter( const char *pszQueryIn )
398 
399 {
400     CPLFree(m_pszAttrQueryString);
401     m_pszAttrQueryString = (pszQueryIn) ? CPLStrdup(pszQueryIn) : nullptr;
402 
403     if( (pszQueryIn== nullptr && this->pszQuery == nullptr)
404         || (pszQueryIn != nullptr && this->pszQuery != nullptr
405             && EQUAL(pszQueryIn,this->pszQuery)) )
406         return OGRERR_NONE;
407 
408     CPLFree( this->pszQuery );
409     this->pszQuery = CPLStrdup( pszQueryIn );
410 
411     ClearQuery();
412 
413     return OGRERR_NONE;
414 }
415 
416 /************************************************************************/
417 /*                           TestCapability()                           */
418 /************************************************************************/
419 
TestCapability(const char * pszCap)420 int OGRIDBTableLayer::TestCapability( const char * pszCap )
421 
422 {
423     if( EQUAL(pszCap,OLCSequentialWrite) ||
424         EQUAL(pszCap,OLCRandomWrite) )
425         return bUpdateAccess;
426 
427     else if( EQUAL(pszCap,OLCRandomRead) )
428         return TRUE;
429 
430     else
431         return OGRIDBLayer::TestCapability( pszCap );
432 }
433 
434 /************************************************************************/
435 /*                          GetFeatureCount()                           */
436 /*                                                                      */
437 /*      If a spatial filter is in effect, we turn control over to       */
438 /*      the generic counter.  Otherwise we return the total count.      */
439 /*      Eventually we should consider implementing a more efficient     */
440 /*      way of counting features matching a spatial query.              */
441 /************************************************************************/
442 
GetFeatureCount(int bForce)443 GIntBig OGRIDBTableLayer::GetFeatureCount( int bForce )
444 
445 {
446     return OGRIDBLayer::GetFeatureCount( bForce );
447 }
448 
449 /************************************************************************/
450 /*                           GetSpatialRef()                            */
451 /*                                                                      */
452 /*      We override this to try and fetch the table SRID from the       */
453 /*      geometry_columns table if the srsid is -2 (meaning we           */
454 /*      haven't yet even looked for it).                                */
455 /************************************************************************/
456 
GetSpatialRef()457 OGRSpatialReference *OGRIDBTableLayer::GetSpatialRef()
458 
459 {
460     if( nSRSId == -2 )
461     {
462         nSRSId = -1;
463 
464         if ( ! pszGeomColumn )
465             return nullptr;
466 
467         CPLString osCmd;
468         osCmd.Printf( " SELECT FIRST 1 srid, trim(srtext)"
469                       " FROM spatial_ref_sys, %s"
470                       " WHERE srid = ST_Srid(%s) ",
471                       poFeatureDefn->GetName(), pszGeomColumn );
472 
473         ITCursor oSridCur( *poDS->GetConnection() );
474 
475         if( oSridCur.Prepare( osCmd.c_str() )&&
476             oSridCur.Open( ITCursor::ReadOnly ) )
477         {
478             ITRow * row = static_cast<ITRow *>( oSridCur.NextRow() );
479             if ( row && ! row->IsNull() )
480             {
481                 nSRSId = atoi(row->Column(0)->Printable());
482                 const char * wkt = row->Column(1)->Printable();
483 
484                 if ( poSRS )
485                 {
486                     // Hmm ... it should be null
487                     delete poSRS;
488                 }
489                 poSRS = new OGRSpatialReference();
490                 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
491                 if ( poSRS->importFromWkt( wkt ) != OGRERR_NONE )
492                 {
493                     CPLError( CE_Warning, CPLE_AppDefined,
494                               "Error parse srs wkt: %s", wkt );
495                     delete poSRS;
496                     poSRS = nullptr;
497                 }
498             }
499         }
500     }
501 
502     return OGRIDBLayer::GetSpatialRef();
503 }
504 
505 #if 0
506 OGRErr OGRIDBTableLayer::ISetFeature( OGRFeature *poFeature )
507 {
508     OGRErr eErr(OGRERR_FAILURE);
509 
510     if ( ! bUpdateAccess )
511     {
512         CPLError( CE_Failure, CPLE_AppDefined,
513                   "Error update feature. Layer is read only." );
514         return eErr;
515     }
516 
517     if( nullptr == poFeature )
518     {
519         CPLError( CE_Failure, CPLE_AppDefined,
520                   "NULL pointer to OGRFeature passed to SetFeature()." );
521         return eErr;
522     }
523 
524     if( poFeature->GetFID() == OGRNullFID )
525     {
526         CPLError( CE_Failure, CPLE_AppDefined,
527                   "FID required on features given to SetFeature()." );
528         return eErr;
529     }
530 
531     ITStatement oQuery( *poDS->GetConnection() );
532 
533     int bUpdateGeom = TRUE;
534     OGRwkbGeometryType nGeomType = poFeature->GetGeometryRef()->getGeometryType();
535     CPLString osGeomFunc;
536     int nSrid = 0; // FIXME Obtain geometry SRID
537 
538     switch (nGeomType)
539     {
540         case wkbPoint:
541             osGeomFunc = "ST_PointFromText";
542             break;
543         case wkbLineString:
544             osGeomFunc = "ST_LineFromText";
545             break;
546         case wkbPolygon:
547             osGeomFunc = "ST_PolyFromText";
548             break;
549         case wkbMultiPoint:
550             osGeomFunc = "ST_MPointFromText";
551             break;
552         case wkbMultiLineString:
553             osGeomFunc = "ST_MLineFromText";
554             break;
555         case wkbMultiPolygon:
556             osGeomFunc = "ST_MPolyFromText";
557             break;
558         default:
559             bUpdateGeom = FALSE;
560             CPLDebug("OGR_IDB", "SetFeature(): Unknown geometry type. Geometry will not be updated.");
561     }
562 
563     // Create query
564     CPLString osSql;
565     CPLString osFields;
566 
567     if ( pszGeomColumn && bUpdateGeom )
568     {
569         osFields.Printf( "%s = %s( ?, %d )", pszGeomColumn, osGeomFunc.c_str(), nSrid );
570     }
571 
572     for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
573     {
574         const char * pszFieldName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
575 
576         // skip fid column from update
577         if ( EQUAL( pszFIDColumn, pszFieldName ) )
578             continue;
579 
580         if ( ! osFields.empty() )
581         {
582             osFields += ",";
583         }
584 
585         osFields += pszFieldName;
586         osFields += "=?";
587     }
588 
589     osSql.Printf( "UPDATE %s SET %s WHERE %s = %d",
590                   poFeatureDefn->GetName(),
591                   osFields.c_str(),
592                   pszFIDColumn,
593                   poFeature->GetFID() );
594 
595     if ( ! oQuery.Prepare( osSql.c_str() ) )
596     {
597         CPLError( CE_Failure, CPLE_AppDefined,
598                   "Error prepare SQL.\n%s",osSql.c_str() );
599         return eErr;
600     }
601 
602     int iParam = 0;
603 
604     if ( pszGeomColumn && bUpdateGeom )
605     {
606         ITValue * par = oQuery.Param( iParam ); // it should be a geom value
607         if ( ! par )
608         {
609             CPLError( CE_Failure, CPLE_AppDefined,
610                       "Error prepare geom param");
611             return eErr;
612         }
613 
614         OGRGeometry * poGeom = poFeature->GetGeometryRef();
615         char * wkt;
616         poGeom->exportToWkt( &wkt );
617 
618         if( ! par->FromPrintable( wkt ) )
619         {
620             CPLError( CE_Failure, CPLE_AppDefined,
621                       "Error prepare geom param");
622             par->Release();
623             return eErr;
624         }
625 
626         CPLFree( wkt );
627         par->Release();
628 
629         iParam++;
630     }
631 
632     for ( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
633     {
634         ITValue * par = oQuery.Param( iParam );
635         if ( ! par )
636         {
637             CPLError( CE_Failure, CPLE_AppDefined,
638                       "Error prepare param %d", iParam);
639             return eErr;
640         }
641 
642         if ( ! poFeature->IsFieldSetAndNotNull( i ) )
643         {
644             if ( ! par->SetNull() )
645             {
646                 CPLError( CE_Failure, CPLE_AppDefined,
647                         "Error set param %d to NULL", iParam);
648                 par->Release();
649                 return eErr;
650             }
651             par->Release();
652             continue;
653         }
654 
655         ITConversions * cv = 0;
656         bool res = FALSE;
657 
658         if ( par->QueryInterface( ITConversionsIID, (void **) &cv) !=
659              IT_QUERYINTERFACE_SUCCESS )
660         {
661             CPLError( CE_Failure, CPLE_AppDefined,
662                     "Error prepare param %d", iParam);
663             par->Release();
664             return eErr;
665         }
666 
667         switch ( poFeatureDefn->GetFieldDefn( i )->GetType() )
668         {
669             case OFTInteger:
670                 res = cv->ConvertFrom( poFeature->GetFieldAsInteger( i ) );
671                 break;
672 
673             case OFTReal:
674                 res = cv->ConvertFrom( poFeature->GetFieldAsDouble( i ) );
675                 break;
676 
677             case OFTIntegerList:
678             case OFTRealList:
679             case OFTStringList:
680                 // FIXME Prepare array of values field
681                 //cv->ConvertFrom( poFeature->GetFieldAsStringList( i ) );
682                 //break;
683 
684             case OFTBinary:
685                 // FIXME Prepare binary field
686 
687             case OFTString:
688             case OFTDate:
689             case OFTTime:
690             case OFTDateTime:
691                 res = cv->ConvertFrom( poFeature->GetFieldAsString( i ) );
692                 break;
693             default:
694                 CPLError( CE_Failure, CPLE_AppDefined,
695                         "Error prepare param %d. Unknown data type.", iParam);
696                 cv->Release();
697                 par->Release();
698                 return eErr;
699         }
700         if ( res != TRUE )
701             CPLError( CE_Failure, CPLE_AppDefined,
702                       "Error prepare param.");
703         cv->Release();
704         par->Release();
705     }
706 
707     CPLDebug( "OGR_IDB", "ExecuteSQL(%s)", oQuery.QueryText().Data() );
708     if( !oQuery.Exec() )
709     {
710         CPLError( CE_Failure, CPLE_AppDefined, "Error update Feature.");
711         return eErr;
712     }
713 
714     return OGRERR_NONE;
715 }
716 
717 #endif
718 
ISetFeature(OGRFeature * poFeature)719 OGRErr OGRIDBTableLayer::ISetFeature( OGRFeature *poFeature )
720 {
721     OGRErr eErr(OGRERR_FAILURE);
722 
723     if ( ! bUpdateAccess )
724     {
725         CPLError( CE_Failure, CPLE_AppDefined,
726                   "Error update feature. Layer is read only." );
727         return eErr;
728     }
729 
730     if( nullptr == poFeature )
731     {
732         CPLError( CE_Failure, CPLE_AppDefined,
733                   "NULL pointer to OGRFeature passed to SetFeature()." );
734         return eErr;
735     }
736 
737     if( poFeature->GetFID() == OGRNullFID )
738     {
739         CPLError( CE_Failure, CPLE_AppDefined,
740                   "FID required on features given to SetFeature()." );
741         return eErr;
742     }
743 
744     ITStatement oQuery( *poDS->GetConnection() );
745 
746     int bUpdateGeom = TRUE;
747     CPLString osGeomFunc;
748 
749     if ( poFeature->GetGeometryRef() )
750     {
751         OGRwkbGeometryType nGeomType = poFeature->GetGeometryRef()->getGeometryType();
752 
753         switch (nGeomType)
754         {
755             case wkbPoint:
756                 osGeomFunc = "ST_PointFromText";
757                 break;
758             case wkbLineString:
759                 osGeomFunc = "ST_LineFromText";
760                 break;
761             case wkbPolygon:
762                 osGeomFunc = "ST_PolyFromText";
763                 break;
764             case wkbMultiPoint:
765                 osGeomFunc = "ST_MPointFromText";
766                 break;
767             case wkbMultiLineString:
768                 osGeomFunc = "ST_MLineFromText";
769                 break;
770             case wkbMultiPolygon:
771                 osGeomFunc = "ST_MPolyFromText";
772                 break;
773             default:
774                 bUpdateGeom = FALSE;
775                 CPLDebug("OGR_IDB", "SetFeature(): Unknown geometry type. Geometry will not be updated.");
776         }
777     }
778     else
779     {
780         bUpdateGeom = FALSE;
781     }
782 
783     // Create query
784     CPLString osSql;
785     CPLString osFields;
786 
787     if ( pszGeomColumn && bUpdateGeom )
788     {
789         OGRGeometry * poGeom = poFeature->GetGeometryRef();
790         char * wkt;
791         poGeom->exportToWkt( &wkt );
792 
793         osFields.Printf( "%s = %s( '%s', %d )", pszGeomColumn, osGeomFunc.c_str(), wkt, nSRSId );
794 
795         CPLFree( wkt );
796     }
797 
798     for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
799     {
800         const char * pszFieldName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
801 
802         // skip fid column from update
803         if ( EQUAL( pszFIDColumn, pszFieldName ) )
804             continue;
805 
806         if ( ! osFields.empty() )
807         {
808             osFields += ",";
809         }
810 
811         osFields += pszFieldName;
812         osFields += "=";
813 
814         if ( ! poFeature->IsFieldSetAndNotNull( i ) )
815         {
816             osFields += "NULL";
817             continue;
818         }
819 
820         CPLString osVal;
821 
822         switch ( poFeatureDefn->GetFieldDefn( i )->GetType() )
823         {
824             case OFTInteger:
825                 osVal.Printf( "%d", poFeature->GetFieldAsInteger( i ) );
826                 break;
827 
828             case OFTReal:
829                 if ( poFeatureDefn->GetFieldDefn( i )->GetPrecision() )
830                 {
831                     // have a decimal format width.precision
832                     CPLString osFormatString;
833                     osFormatString.Printf( "%%%d.%df",
834                                            poFeatureDefn->GetFieldDefn( i )->GetWidth(),
835                                            poFeatureDefn->GetFieldDefn( i )->GetPrecision() );
836                     osVal.Printf( osFormatString.c_str(), poFeature->GetFieldAsDouble( i ) );
837                 }
838                 else
839                     osVal.Printf( "%f", poFeature->GetFieldAsDouble( i ) );
840                 break;
841 
842             case OFTIntegerList:
843             case OFTRealList:
844             case OFTStringList:
845                 // FIXME Prepare array of values field
846                 //cv->ConvertFrom( poFeature->GetFieldAsStringList( i ) );
847                 //break;
848 
849             case OFTBinary:
850                 // FIXME Prepare binary field
851 
852             case OFTString:
853             case OFTDate:
854             case OFTTime:
855             case OFTDateTime:
856             default:
857                 osVal.Printf( "'%s'", poFeature->GetFieldAsString( i ) );
858                 break;
859         }
860         osFields += osVal;
861     }
862 
863     osSql.Printf( "UPDATE %s SET %s WHERE %s = " CPL_FRMT_GIB,
864                   poFeatureDefn->GetName(),
865                   osFields.c_str(),
866                   pszFIDColumn,
867                   poFeature->GetFID() );
868 
869     if ( ! oQuery.Prepare( osSql.c_str() ) )
870     {
871         CPLError( CE_Failure, CPLE_AppDefined,
872                   "Error prepare SQL.\n%s",osSql.c_str() );
873         return eErr;
874     }
875 
876     CPLDebug( "OGR_IDB", "Exec(%s)", oQuery.QueryText().Data() );
877     if( !oQuery.Exec() )
878     {
879         CPLError( CE_Failure, CPLE_AppDefined, "Error update Feature.");
880         return eErr;
881     }
882 
883     return OGRERR_NONE;
884 }
885 
ICreateFeature(OGRFeature * poFeature)886 OGRErr OGRIDBTableLayer::ICreateFeature( OGRFeature *poFeature )
887 {
888     OGRErr eErr(OGRERR_FAILURE);
889 
890     if ( ! bUpdateAccess )
891     {
892         CPLError( CE_Failure, CPLE_AppDefined,
893                   "Error create feature. Layer is read only." );
894         return eErr;
895     }
896 
897     if( nullptr == poFeature )
898     {
899         CPLError( CE_Failure, CPLE_AppDefined,
900                   "NULL pointer to OGRFeature passed to CreateFeature()." );
901         return eErr;
902     }
903 
904     if( poFeature->GetFID() != OGRNullFID && pszFIDColumn== nullptr )
905     {
906         CPLError( CE_Failure, CPLE_AppDefined,
907                   "FID ignored on feature given to CreateFeature(). Unknown FID column." );
908         return eErr;
909     }
910 
911     int bUpdateGeom = TRUE;
912     CPLString osGeomFunc;
913 
914     if ( poFeature->GetGeometryRef() )
915     {
916         OGRwkbGeometryType nGeomType = poFeature->GetGeometryRef()->getGeometryType();
917 
918         switch (nGeomType)
919         {
920             case wkbPoint:
921                 osGeomFunc = "ST_PointFromText";
922                 break;
923             case wkbLineString:
924                 osGeomFunc = "ST_LineFromText";
925                 break;
926             case wkbPolygon:
927                 osGeomFunc = "ST_PolyFromText";
928                 break;
929             case wkbMultiPoint:
930                 osGeomFunc = "ST_MPointFromText";
931                 break;
932             case wkbMultiLineString:
933                 osGeomFunc = "ST_MLineFromText";
934                 break;
935             case wkbMultiPolygon:
936                 osGeomFunc = "ST_MPolyFromText";
937                 break;
938             default:
939                 bUpdateGeom = FALSE;
940                 CPLDebug("OGR_IDB", "SetFeature(): Unknown geometry type. Geometry will not be updated.");
941         }
942     }
943     else
944         bUpdateGeom = FALSE;
945 
946     // Create query
947     CPLString osSql;
948     CPLString osFields;
949     CPLString osValues;
950 
951     if ( pszGeomColumn && bUpdateGeom )
952     {
953         OGRGeometry * poGeom = poFeature->GetGeometryRef();
954         char * wkt;
955         poGeom->exportToWkt( &wkt );
956 
957         osFields += pszGeomColumn;
958         osValues.Printf( "%s( '%s', %d )", osGeomFunc.c_str(), wkt, nSRSId );
959 
960         CPLFree( wkt );
961     }
962 
963     for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
964     {
965         const char * pszFieldName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
966 
967         // Skip NULL fields
968         if ( ! poFeature->IsFieldSetAndNotNull( i ) )
969         {
970             continue;
971         }
972 
973         if ( ! osFields.empty() )
974         {
975             osFields += ",";
976             osValues += ",";
977         }
978 
979         osFields += pszFieldName;
980 
981         CPLString osVal;
982 
983         switch ( poFeatureDefn->GetFieldDefn( i )->GetType() )
984         {
985             case OFTInteger:
986                 osVal.Printf( "%d", poFeature->GetFieldAsInteger( i ) );
987                 break;
988 
989             case OFTReal:
990                 if ( poFeatureDefn->GetFieldDefn( i )->GetPrecision() )
991                 {
992                     // have a decimal format width.precision
993                     CPLString osFormatString;
994                     osFormatString.Printf( "%%%d.%df",
995                                            poFeatureDefn->GetFieldDefn( i )->GetWidth(),
996                                            poFeatureDefn->GetFieldDefn( i )->GetPrecision() );
997                     osVal.Printf( osFormatString.c_str(), poFeature->GetFieldAsDouble( i ) );
998                 }
999                 else
1000                     osVal.Printf( "%f", poFeature->GetFieldAsDouble( i ) );
1001                 break;
1002 
1003             case OFTIntegerList:
1004             case OFTRealList:
1005             case OFTStringList:
1006                 // FIXME Prepare array of values field
1007                 //cv->ConvertFrom( poFeature->GetFieldAsStringList( i ) );
1008                 //break;
1009 
1010             case OFTBinary:
1011                 // FIXME Prepare binary field
1012 
1013             case OFTString:
1014             case OFTDate:
1015             case OFTTime:
1016             case OFTDateTime:
1017             default:
1018                 osVal.Printf( "'%s'", poFeature->GetFieldAsString( i ) );
1019                 break;
1020         }
1021         osValues += osVal;
1022     }
1023 
1024     osSql.Printf( "INSERT INTO %s (%s) VALUES (%s)",
1025                   poFeatureDefn->GetName(),
1026                   osFields.c_str(),
1027                   osValues.c_str() );
1028 
1029     ITStatement oQuery( *poDS->GetConnection() );
1030 
1031     if ( ! oQuery.Prepare( osSql.c_str() ) )
1032     {
1033         CPLError( CE_Failure, CPLE_AppDefined,
1034                   "Error prepare SQL.\n%s",osSql.c_str() );
1035         return eErr;
1036     }
1037 
1038     CPLDebug( "OGR_IDB", "Exec(%s)", oQuery.QueryText().Data() );
1039     if( !oQuery.Exec() )
1040     {
1041         CPLError( CE_Failure, CPLE_AppDefined, "Error create Feature.");
1042         return eErr;
1043     }
1044 
1045     ITQuery oFidQuery( *poDS->GetConnection() );
1046     osSql.Printf( "SELECT MAX(%s) from %s", pszFIDColumn, poFeatureDefn->GetName() );
1047 
1048     CPLDebug( "OGR_IDB", "Exec(%s)", osSql.c_str() );
1049 
1050     ITRow * row = oFidQuery.ExecOneRow( osSql.c_str() );
1051     if( ! row || row->NumColumns() < 1 )
1052     {
1053         CPLError( CE_Failure, CPLE_AppDefined, "Error create Feature.");
1054         return eErr;
1055     }
1056 
1057     int fid = atoi( row->Column(0)->Printable() );
1058 
1059     if ( fid > 0 )
1060         poFeature->SetFID( fid );
1061     else
1062     {
1063         CPLError( CE_Failure, CPLE_AppDefined, "Error create Feature. Unable to get new fid" );
1064         return eErr;
1065     }
1066 
1067     return OGRERR_NONE;
1068 }
1069