1 /*****************************************************************************
2  *
3  * Project:  DB2 Spatial driver
4  * Purpose:  Implements OGRDB2TableLayer class, access to an existing table.
5  * Author:   David Adler, dadler at adtechgeospatial dot com
6  *
7  *****************************************************************************
8  * Copyright (c) 2010, Tamas Szekeres
9  * Copyright (c) 2015, David Adler
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include "cpl_conv.h"
30 #include "ogr_db2.h"
31 
32 CPL_CVSID("$Id: ogrdb2tablelayer.cpp 8c3e4ef55212f20eec95aa7e12ba5d48dacfdc47 2020-10-01 21:20:51 +0200 Even Rouault $")
33 
34 /************************************************************************/
35 /*                         OGRDB2AppendEscaped( )                     */
36 /************************************************************************/
37 
OGRDB2AppendEscaped(OGRDB2Statement * poStatement,const char * pszStrValue)38 void OGRDB2AppendEscaped( OGRDB2Statement* poStatement,
39                           const char* pszStrValue)
40 {
41     if (!pszStrValue)
42     {
43         poStatement->Append("null");
44         return;
45     }
46 
47     size_t  iIn, iOut , nTextLen = strlen(pszStrValue);
48     char    *pszEscapedText = (char *) VSIMalloc(nTextLen*2 + 3);
49 
50     pszEscapedText[0] = '\'';
51 
52     for( iIn = 0, iOut = 1; iIn < nTextLen; iIn++ )
53     {
54         switch( pszStrValue[iIn] )
55         {
56         case '\'':
57             pszEscapedText[iOut++] = '\''; // double quote
58             pszEscapedText[iOut++] = pszStrValue[iIn];
59             break;
60 
61         default:
62             pszEscapedText[iOut++] = pszStrValue[iIn];
63             break;
64         }
65     }
66 
67     pszEscapedText[iOut++] = '\'';
68 
69     pszEscapedText[iOut] = '\0';
70 
71     poStatement->Append(pszEscapedText);
72 
73     CPLFree( pszEscapedText );
74 }
75 
76 /************************************************************************/
77 /*                          OGRDB2TableLayer()                 */
78 /************************************************************************/
79 
OGRDB2TableLayer(OGRDB2DataSource * poDSIn)80 OGRDB2TableLayer::OGRDB2TableLayer( OGRDB2DataSource *poDSIn ) :
81     eGeomType( wkbNone )
82 
83 {
84     poDS = poDSIn;
85     m_poStmt = nullptr;
86     m_poPrepStmt = nullptr;
87     m_pszQuery = nullptr;
88     m_nFeaturesRead = 0;
89 
90     bUpdateAccess = TRUE;
91 
92     iNextShapeId = 0;
93 
94     nSRSId = -1;
95 
96     poFeatureDefn = nullptr;
97 
98     pszTableName = nullptr;
99     m_pszLayerName = nullptr;
100     pszSchemaName = nullptr;
101     pszFIDColumn = nullptr;
102 
103     bLaunderColumnNames = false;
104     bPreservePrecision = false;
105     bNeedSpatialIndex = false;
106     m_iSrs = 0;
107 }
108 
109 /************************************************************************/
110 /*                          ~OGRDB2TableLayer()                */
111 /************************************************************************/
112 
~OGRDB2TableLayer()113 OGRDB2TableLayer::~OGRDB2TableLayer()
114 
115 {
116     CPLDebug("OGRDB2TableLayer::~OGRDB2TableLayer","entering");
117     CPLFree( pszTableName );
118     CPLFree( m_pszLayerName );
119     CPLFree( pszSchemaName );
120 
121     CPLFree( m_pszQuery );
122     ClearStatement();
123     if( m_poPrepStmt != nullptr )
124     {
125         delete m_poPrepStmt;
126         m_poPrepStmt = nullptr;
127     }
128     CPLDebug("OGRDB2TableLayer::~OGRDB2TableLayer","exiting");
129 }
130 
131 /************************************************************************/
132 /*                               GetName()                              */
133 /************************************************************************/
134 
GetName()135 const char *OGRDB2TableLayer::GetName()
136 
137 {
138     return m_pszLayerName;
139 }
140 
141 /************************************************************************/
142 /*                             GetLayerDefn()                           */
143 /************************************************************************/
GetLayerDefn()144 OGRFeatureDefn* OGRDB2TableLayer::GetLayerDefn()
145 {
146     if (poFeatureDefn)
147         return poFeatureDefn;
148 
149     OGRDB2Session *poSession = poDS->GetSession();
150     /* -------------------------------------------------------------------- */
151     /*      Do we have a simple primary key?                                */
152     /* -------------------------------------------------------------------- */
153     OGRDB2Statement oGetKey( poSession );
154     CPLDebug( "OGR_DB2TableLayer::GetLayerDefn",
155               "pszTableName: %s; pszSchemaName: %s",
156               pszTableName, pszSchemaName);
157     if( oGetKey.GetPrimaryKeys( pszTableName, nullptr, pszSchemaName ) ) {
158         if( oGetKey.Fetch() )
159         {
160             pszFIDColumn = CPLStrdup(oGetKey.GetColData( 3 ));
161             if( oGetKey.Fetch() ) // more than one field in key!
162             {
163                 oGetKey.Clear();
164                 CPLFree( pszFIDColumn );
165                 pszFIDColumn = nullptr;
166                 CPLDebug( "OGR_DB2TableLayer::GetLayerDefn",
167                           "Table %s has multiple primary key fields, "
168                           "ignoring them all.", pszTableName );
169             } else {
170                 // Attempt to get the 'identity' and 'generated' information
171                 // from syscat.columns. This is only valid on DB2 LUW so if it
172                 // fails, we assume that we are running on z/OS.
173                 OGRDB2Statement oStatement = OGRDB2Statement(
174                                                  poDS->GetSession());
175                 oStatement.Appendf( "select identity, generated "
176                                     "from syscat.columns "
177                                     "where tabschema = '%s' "
178                                     "and tabname = '%s' and colname = '%s'",
179                                     pszSchemaName, pszTableName,
180                                     pszFIDColumn );
181 
182                 if( oStatement.DB2Execute("OGR_DB2TableLayer::GetLayerDefn") )
183                 {
184                     if( oStatement.Fetch() )
185                     {
186                         if ( oStatement.GetColData( 0 )
187                                 && EQUAL(oStatement.GetColData( 0 ), "Y")) {
188                             bIsIdentityFid = TRUE;
189                             if ( oStatement.GetColData( 1 ) ) {
190                                 cGenerated = oStatement.GetColData( 1 )[0];
191                             }
192                         }
193                     }
194                 } else {
195                     CPLDebug( "OGR_DB2TableLayer::GetLayerDefn",
196                               "Must be z/OS");
197                     // on z/OS, get all the column data for table and loop
198                     // through looking for the FID column, then check the
199                     // column default information for 'IDENTIY' and 'ALWAYS'
200                     if (oGetKey.GetColumns(pszTableName, nullptr, pszSchemaName))
201                     {
202                         CPLDebug( "OGR_DB2TableLayer::GetLayerDefn",
203                                   "GetColumns succeeded");
204                         CPLDebug( "OGR_DB2TableLayer::GetLayerDefn",
205                                   "ColName[0]: '%s'",oGetKey.GetColName(0));
206                         for (int idx = 0; idx < oGetKey.GetColCount(); idx++)
207                         {
208                             CPLDebug( "OGR_DB2TableLayer::GetLayerDefn",
209                                       "ColName[0]: '%s'",
210                                       oGetKey.GetColName(idx));
211                             if (!strcmp(pszFIDColumn,oGetKey.GetColName(idx)))
212                             {
213                                 CPLDebug( "OGR_DB2TableLayer::GetLayerDefn",
214                                           "ColDef[0]: '%s'",
215                                           oGetKey.GetColColumnDef(idx));
216                                 if (strstr(oGetKey.GetColColumnDef(idx),
217                                            "IDENTITY"))
218                                     bIsIdentityFid = TRUE;
219                                 if (strstr(oGetKey.GetColColumnDef(idx),
220                                            "ALWAYS"))
221                                     cGenerated = 'A';
222                             }
223                         }
224                     }
225                 }
226                 CPLDebug( "OGR_DB2TableLayer::GetLayerDefn",
227                           "FIDColumn: '%s', identity: '%d', generated: '%c'",
228                           pszFIDColumn, bIsIdentityFid, cGenerated);
229             }
230         }
231     } else
232         CPLDebug( "OGR_DB2TableLayer::GetLayerDefn", "GetPrimaryKeys failed");
233 
234     /* -------------------------------------------------------------------- */
235     /*      Get the column definitions for this table.                      */
236     /* -------------------------------------------------------------------- */
237     OGRDB2Statement oGetCol( poSession );
238     CPLErr eErr;
239 
240     if( !oGetCol.GetColumns( pszTableName, "", pszSchemaName ) )
241         return nullptr;
242 
243     eErr = BuildFeatureDefn( m_pszLayerName, &oGetCol );
244     if( eErr != CE_None )
245         return nullptr;
246 
247     if (eGeomType != wkbNone)
248         poFeatureDefn->SetGeomType(eGeomType);
249 
250     if ( GetSpatialRef() && poFeatureDefn->GetGeomFieldCount() == 1)
251         poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef( poSRS );
252 
253     if( poFeatureDefn->GetFieldCount() == 0 &&
254             pszFIDColumn == nullptr && pszGeomColumn == nullptr )
255     {
256         CPLError( CE_Failure, CPLE_AppDefined,
257                   "No column definitions found for table '%s', "
258                   "layer not usable.",
259                   m_pszLayerName );
260         return nullptr;
261     }
262 
263     /* -------------------------------------------------------------------- */
264     /*      If we got a geometry column, does it exist?  Is it binary?      */
265     /* -------------------------------------------------------------------- */
266 
267     if( pszGeomColumn != nullptr )
268     {
269         poFeatureDefn->GetGeomFieldDefn(0)->SetName( pszGeomColumn );
270         int iColumn = oGetCol.GetColId( pszGeomColumn );
271         if( iColumn < 0 )
272         {
273             CPLError( CE_Failure, CPLE_AppDefined,
274                       "Column %s requested for geometry, "
275                       "but it does not exist.",
276                       pszGeomColumn );
277             CPLFree( pszGeomColumn );
278             pszGeomColumn = nullptr;
279         }
280     }
281 
282     return poFeatureDefn;
283 }
284 
285 /************************************************************************/
286 /*                             Initialize()                             */
287 /************************************************************************/
288 
Initialize(const char * pszSchema,const char * pszLayerName,const char * pszGeomCol,CPL_UNUSED int nCoordDimension,int nSRId,const char * pszSRText,OGRwkbGeometryType eType)289 CPLErr OGRDB2TableLayer::Initialize( const char *pszSchema,
290                                      const char *pszLayerName,
291                                      const char *pszGeomCol,
292                                      CPL_UNUSED int nCoordDimension,
293                                      int nSRId,
294                                      const char *pszSRText,
295                                      OGRwkbGeometryType eType )
296 {
297 //    CPLFree( pszFIDColumn );
298     pszFIDColumn = nullptr;
299 
300     CPLDebug( "OGR_DB2TableLayer::Initialize",
301               "schema: '%s', layerName: '%s', geomCol: '%s'",
302               pszSchema, pszLayerName, pszGeomCol);
303     CPLDebug( "OGR_DB2TableLayer::Initialize",
304               "nSRId: '%d', eType: '%d', srText: '%s'",
305               nSRId, eType, pszSRText);
306 
307     /* -------------------------------------------------------------------- */
308     /*      Parse out schema name if present in layer.  We assume a         */
309     /*      schema is provided if there is a dot in the name, and that      */
310     /*      it is in the form <schema>.<tablename>                          */
311     /* -------------------------------------------------------------------- */
312     const char *pszDot = strstr(pszLayerName,".");
313     if( pszDot != nullptr )
314     {
315         pszTableName = CPLStrdup(pszDot + 1);
316         pszSchemaName = CPLStrdup(pszLayerName);
317         pszSchemaName[pszDot - pszLayerName] = '\0';
318         this->m_pszLayerName = CPLStrdup(pszLayerName);
319     }
320     else
321     {
322         pszTableName = CPLStrdup(pszLayerName);
323         pszSchemaName = CPLStrdup(pszSchema);
324         this->m_pszLayerName = CPLStrdup(CPLSPrintf("%s.%s", pszSchemaName,
325                                          pszTableName));
326     }
327     SetDescription( this->m_pszLayerName );
328     CPLDebug( "OGR_DB2TableLayer::Initialize",
329               "this->m_pszLayerName: '%s', layerName: '%s', geomCol: '%s'",
330               this->m_pszLayerName, pszLayerName, pszGeomCol);
331     /* -------------------------------------------------------------------- */
332     /*      Have we been provided a geometry column?                        */
333     /* -------------------------------------------------------------------- */
334 //    CPLFree( pszGeomColumn ); LATER
335     if( pszGeomCol == nullptr )
336         GetLayerDefn(); /* fetch geom column if not specified */
337     else
338         pszGeomColumn = CPLStrdup( pszGeomCol );
339 
340     if (eType != wkbNone)
341         eGeomType = eType;
342 
343     /* -------------------------------------------------------------------- */
344     /*             Try to find out the spatial reference                    */
345     /* -------------------------------------------------------------------- */
346 
347     nSRSId = nSRId;
348 
349     if (pszSRText)
350     {
351         /* Process srtext directly if specified */
352         poSRS = new OGRSpatialReference();
353         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
354         if( poSRS->importFromWkt( (char**)&pszSRText ) != OGRERR_NONE )
355         {
356             delete poSRS;
357             poSRS = nullptr;
358         }
359     }
360 
361     if (!poSRS)
362     {
363         if (nSRSId < 0)
364             nSRSId = FetchSRSId();
365 
366         GetSpatialRef();
367     }
368 
369     return CE_None;
370 }
371 
372 /************************************************************************/
373 /*                         FetchSRSId()                                 */
374 /************************************************************************/
375 
FetchSRSId()376 int OGRDB2TableLayer::FetchSRSId()
377 {
378     OGRDB2Statement oStatement = OGRDB2Statement( poDS->GetSession() );
379 
380 // first try to get the srid from st_geometry_columns
381 // if the spatial column was registered
382     oStatement.Appendf( "select srs_id from db2gse.st_geometry_columns "
383                         "where table_schema = '%s' and table_name = '%s'",
384                         pszSchemaName, pszTableName );
385 
386     if( oStatement.DB2Execute("OGRDB2TableLayer::FetchSRSId")
387             && oStatement.Fetch() )
388     {
389         if ( oStatement.GetColData( 0 ) )
390             nSRSId = atoi( oStatement.GetColData( 0 ) );
391     }
392 
393 // If it was not found there, try to get it from the data table.
394 // This only works if there is spatial data in the first row.
395     if (nSRSId < 0 )
396     {
397         oStatement.Clear();
398         oStatement.Appendf("select db2gse.st_srid(%s) from %s.%s "
399                            "fetch first row only",
400                            pszGeomColumn, pszSchemaName, pszTableName);
401         if ( oStatement.DB2Execute("OGR_DB2TableLayer::FetchSRSId")
402                 && oStatement.Fetch() )
403         {
404             if ( oStatement.GetColData( 0 ) )
405                 nSRSId = atoi( oStatement.GetColData( 0 ) );
406         }
407     }
408     CPLDebug( "OGR_DB2TableLayer::FetchSRSId", "nSRSId: '%d'",
409               nSRSId);
410     return nSRSId;
411 }
412 
413 /************************************************************************/
414 /*                       CreateSpatialIndex()                           */
415 /*                                                                      */
416 /*      Create a spatial index on the geometry column of the layer      */
417 /************************************************************************/
418 
CreateSpatialIndex()419 OGRErr OGRDB2TableLayer::CreateSpatialIndex()
420 {
421     CPLDebug("OGRDB2TableLayer::CreateSpatialIndex","Enter");
422     if (poDS->m_bIsZ) {
423         CPLDebug("OGRDB2TableLayer::CreateSpatialIndex",
424                  "Don't create spatial index on z/OS");
425         return OGRERR_NONE;
426     }
427     GetLayerDefn();
428 
429     OGRDB2Statement oStatement( poDS->GetSession() );
430 
431     OGREnvelope oExt;
432     if (GetExtent(&oExt, TRUE) != OGRERR_NONE)
433     {
434         CPLError( CE_Failure, CPLE_AppDefined,
435                   "Failed to get extent for spatial index." );
436         return OGRERR_FAILURE;
437     }
438     CPLDebug("OGRDB2TableLayer::CreateSpatialIndex",
439              "BOUNDING_BOX =(%.15g, %.15g, %.15g, %.15g)",
440              oExt.MinX, oExt.MinY, oExt.MaxX, oExt.MaxY );
441 
442     oStatement.Appendf("CREATE  INDEX %s.%s_sidx ON %s.%s ( %s ) "
443                        "extend using db2gse.spatial_index(.1,0.5,0)",
444                        pszSchemaName, pszTableName,
445                        pszSchemaName, pszTableName, pszGeomColumn );
446 
447     if( !oStatement.DB2Execute("OGR_DB2TableLayer::CreateSpatialIndex") )
448     {
449         CPLError( CE_Failure, CPLE_AppDefined,
450                   "Failed to create the spatial index, %s.",
451                   poDS->GetSession()->GetLastError());
452         return OGRERR_FAILURE;
453     }
454 
455     return OGRERR_NONE;
456 }
457 
458 /************************************************************************/
459 /*                       DropSpatialIndex()                             */
460 /*                                                                      */
461 /*      Drop the spatial index on the geometry column of the layer      */
462 /************************************************************************/
463 
DropSpatialIndex()464 void OGRDB2TableLayer::DropSpatialIndex()
465 {
466     GetLayerDefn();
467 
468     OGRDB2Statement oStatement( poDS->GetSession() );
469 
470     oStatement.Appendf("DROP INDEX %s.%s",
471                        pszSchemaName, pszTableName);
472 
473     //poDS->GetSession()->BeginTransaction();
474 
475     if( !oStatement.DB2Execute("OGR_DB2TableLayer::DropSpatialIndex") )
476     {
477         CPLError( CE_Failure, CPLE_AppDefined,
478                   "Failed to drop the spatial index, %s.",
479                   poDS->GetSession()->GetLastError());
480         return;
481     }
482 
483     //poDS->GetSession()->CommitTransaction();
484 }
485 
486 /************************************************************************/
487 /*                            BuildFields()                             */
488 /*                                                                      */
489 /*      Build list of fields to fetch, performing any required          */
490 /*      transformations (such as on geometry).                          */
491 /************************************************************************/
492 
BuildFields()493 CPLString OGRDB2TableLayer::BuildFields()
494 
495 {
496     int nColumn = 0;
497     CPLString osFieldList;
498 
499     GetLayerDefn();
500 
501     if( pszFIDColumn && poFeatureDefn->GetFieldIndex( pszFIDColumn ) == -1 )
502     {
503         /* Always get the FID column */
504         osFieldList += " ";
505         osFieldList += pszFIDColumn;
506         osFieldList += " ";
507         ++nColumn;
508     }
509 
510     if( pszGeomColumn && !poFeatureDefn->IsGeometryIgnored())
511     {
512         if( nColumn > 0 )
513             osFieldList += ", ";
514 
515         osFieldList += " db2gse.st_astext(";
516         osFieldList += pszGeomColumn;
517         osFieldList += ") as ";
518         osFieldList += pszGeomColumn;
519 
520         osFieldList += " ";
521 
522         ++nColumn;
523     }
524 
525     if (poFeatureDefn->GetFieldCount() > 0)
526     {
527         /* need to reconstruct the field ordinals list */
528         CPLFree(panFieldOrdinals);
529         panFieldOrdinals = (int *) CPLMalloc( sizeof(int)
530                                     * poFeatureDefn->GetFieldCount() );
531 
532         for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
533         {
534             if ( poFeatureDefn->GetFieldDefn(i)->IsIgnored() )
535                 continue;
536 
537             const char *pszName =poFeatureDefn->GetFieldDefn(i)->GetNameRef();
538 
539             if( nColumn > 0 )
540                 osFieldList += ", ";
541 
542             osFieldList += " ";
543             osFieldList += pszName;
544             osFieldList += " ";
545 
546             panFieldOrdinals[i] = nColumn;
547 
548             ++nColumn;
549         }
550     }
551 
552     return osFieldList;
553 }
554 
555 /************************************************************************/
556 /*                           ClearStatement()                           */
557 /************************************************************************/
558 
ClearStatement()559 void OGRDB2TableLayer::ClearStatement()
560 
561 {
562     if( m_poStmt != nullptr )
563     {
564         delete m_poStmt;
565         m_poStmt = nullptr;
566     }
567 }
568 
569 /************************************************************************/
570 /*                            GetStatement()                            */
571 /************************************************************************/
572 
GetStatement()573 OGRDB2Statement *OGRDB2TableLayer::GetStatement()
574 
575 {
576     if( m_poStmt == nullptr )
577     {
578         m_poStmt = BuildStatement(BuildFields());
579         iNextShapeId = 0;
580     }
581 
582     return m_poStmt;
583 }
584 
585 /************************************************************************/
586 /*                           BuildStatement()                           */
587 /************************************************************************/
588 
BuildStatement(const char * pszColumns)589 OGRDB2Statement* OGRDB2TableLayer::BuildStatement(const char* pszColumns)
590 
591 {
592     OGRDB2Statement* poStatement = new OGRDB2Statement( poDS->GetSession());
593     poStatement->Append( "select " );
594     poStatement->Append( pszColumns );
595     poStatement->Append( " from " );
596     poStatement->Append( pszSchemaName );
597     poStatement->Append( "." );
598     poStatement->Append( pszTableName );
599 
600     /* Append attribute query if we have it */
601     if( m_pszQuery != nullptr )
602         poStatement->Appendf( " where (%s)", m_pszQuery );
603 
604     /* If we have a spatial filter, query on it */
605     if ( m_poFilterGeom != nullptr )
606     {
607         if( m_pszQuery == nullptr )
608             poStatement->Append( " where" );
609         else
610             poStatement->Append( " and" );
611 
612         poStatement->Appendf(" db2gse.envelopesintersect(%s,%.15g,%.15g,"
613                              "%.15g,%.15g, 0) = 1",
614                              pszGeomColumn, m_sFilterEnvelope.MinX,
615                              m_sFilterEnvelope.MinY, m_sFilterEnvelope.MaxX,
616                              m_sFilterEnvelope.MaxY );
617     }
618 
619     if( poStatement->DB2Execute("OGR_DB2TableLayer::BuildStatement") ) {
620         return poStatement;
621     }
622     else
623     {
624         delete poStatement;
625         CPLDebug( "OGR_DB2TableLayer::BuildStatement", "ExecuteSQL Failed" );
626         return nullptr;
627     }
628 }
629 
630 /************************************************************************/
631 /*                            ResetReading()                            */
632 /************************************************************************/
633 
ResetReading()634 void OGRDB2TableLayer::ResetReading()
635 
636 {
637     ClearStatement();
638     OGRDB2Layer::ResetReading();
639 }
640 
641 /************************************************************************/
642 /*                             GetFeature()                             */
643 /************************************************************************/
644 
GetFeature(GIntBig nFeatureId)645 OGRFeature *OGRDB2TableLayer::GetFeature( GIntBig nFeatureId )
646 
647 {
648 
649     if( pszFIDColumn == nullptr )
650         return OGRDB2Layer::GetFeature( nFeatureId );
651 
652     ClearStatement();
653 
654     iNextShapeId = nFeatureId;
655 
656     m_poStmt = new OGRDB2Statement( poDS->GetSession() );
657     CPLString osFields = BuildFields();
658     m_poStmt->Appendf( "select %s from %s where %s = " CPL_FRMT_GIB, osFields.c_str(),
659                        poFeatureDefn->GetName(), pszFIDColumn, nFeatureId );
660 
661     if( !m_poStmt->DB2Execute("OGR_DB2TableLayer::GetFeature") )
662     {
663         delete m_poStmt;
664         m_poStmt = nullptr;
665         return nullptr;
666     }
667 
668     return GetNextRawFeature();
669 }
670 
671 /************************************************************************/
672 /*                         SetAttributeFilter()                         */
673 /************************************************************************/
674 
SetAttributeFilter(const char * pszQuery)675 OGRErr OGRDB2TableLayer::SetAttributeFilter( const char *pszQuery )
676 
677 {
678     CPLFree(m_pszAttrQueryString);
679     m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : nullptr;
680 
681     if( (pszQuery == nullptr && this->m_pszQuery == nullptr)
682             || (pszQuery != nullptr && this->m_pszQuery != nullptr
683                 && EQUAL(pszQuery,this->m_pszQuery)) )
684         return OGRERR_NONE;
685 
686     CPLFree( this->m_pszQuery );
687     this->m_pszQuery = (pszQuery) ? CPLStrdup( pszQuery ) : nullptr;
688 
689     ClearStatement();
690 
691     return OGRERR_NONE;
692 }
693 
694 /************************************************************************/
695 /*                           TestCapability()                           */
696 /************************************************************************/
697 
TestCapability(const char * pszCap)698 int OGRDB2TableLayer::TestCapability( const char * pszCap )
699 
700 {
701     if ( bUpdateAccess )
702     {
703         if( EQUAL(pszCap,OLCSequentialWrite) || EQUAL(pszCap,OLCCreateField)
704                 || EQUAL(pszCap,OLCDeleteFeature) )
705             return TRUE;
706 
707         else if( EQUAL(pszCap,OLCRandomWrite) )
708             return pszFIDColumn != nullptr;
709     }
710 
711     if( EQUAL(pszCap,OLCTransactions) )
712         return TRUE;
713 
714     if( EQUAL(pszCap,OLCIgnoreFields) )
715         return TRUE;
716 
717     if( EQUAL(pszCap,OLCRandomRead) )
718         return pszFIDColumn != nullptr;
719     else if( EQUAL(pszCap,OLCFastFeatureCount) )
720         return TRUE;
721     else
722         return OGRDB2Layer::TestCapability( pszCap );
723 }
724 
725 /************************************************************************/
726 /*                          GetFeatureCount()                           */
727 /************************************************************************/
728 
GetFeatureCount(int bForce)729 GIntBig OGRDB2TableLayer::GetFeatureCount( int bForce )
730 
731 {
732     GetLayerDefn();
733 
734     if( TestCapability(OLCFastFeatureCount) == FALSE )
735         return OGRDB2Layer::GetFeatureCount( bForce );
736 
737     ClearStatement();
738 
739     OGRDB2Statement* poStatement = BuildStatement( "count(*)" );
740 
741     if (poStatement == nullptr || !poStatement->Fetch())
742     {
743         delete poStatement;
744         return OGRDB2Layer::GetFeatureCount( bForce );
745     }
746 
747     int nRet = atoi(poStatement->GetColData( 0 ));
748     delete poStatement;
749     return nRet;
750 }
751 
752 /************************************************************************/
753 /*                            CreateField()                             */
754 /************************************************************************/
755 
CreateField(OGRFieldDefn * poFieldIn,int bApproxOK)756 OGRErr OGRDB2TableLayer::CreateField( OGRFieldDefn *poFieldIn,
757                                       int bApproxOK )
758 
759 {
760     char                szFieldType[256];
761     OGRFieldDefn        oField( poFieldIn );
762 
763     GetLayerDefn();
764 
765     /* -------------------------------------------------------------------- */
766     /*      Do we want to "launder" the column names into DB2               */
767     /*      friendly format?                                                */
768     /* -------------------------------------------------------------------- */
769     if( bLaunderColumnNames )
770     {
771         char    *pszSafeName = poDS->LaunderName( oField.GetNameRef() );
772 
773         oField.SetName( pszSafeName );
774         CPLFree( pszSafeName );
775     }
776 
777     /* -------------------------------------------------------------------- */
778     /*      Identify the DB2 type.                                          */
779     /* -------------------------------------------------------------------- */
780 
781     int fieldType = oField.GetType();
782     CPLDebug("OGR_DB2TableLayer::CreateField","fieldType: %d", fieldType);
783     if( oField.GetType() == OFTInteger )
784     {
785         if( oField.GetWidth() > 0 && bPreservePrecision )
786             snprintf( szFieldType, sizeof(szFieldType), "numeric(%d,0)", oField.GetWidth() );
787         else
788             strcpy( szFieldType, "int" );
789     }
790     else if( oField.GetType() == OFTInteger64 )
791     {
792         if( oField.GetWidth() > 0 && bPreservePrecision )
793             snprintf( szFieldType, sizeof(szFieldType), "numeric(%d,0)", oField.GetWidth() );
794         else
795             strcpy( szFieldType, "bigint" );
796     }
797     else if( oField.GetType() == OFTReal )
798     {
799         if( oField.GetWidth() > 0 && oField.GetPrecision() > 0
800                 && bPreservePrecision )
801             snprintf( szFieldType, sizeof(szFieldType), "numeric(%d,%d)",
802                      oField.GetWidth(), oField.GetPrecision() );
803         else
804             strcpy( szFieldType, "float" );
805     }
806     else if( oField.GetType() == OFTString )
807     {
808         if( oField.GetWidth() == 0 || !bPreservePrecision )
809             strcpy( szFieldType, "varchar(MAX)" );
810         else
811             snprintf( szFieldType, sizeof(szFieldType), "varchar(%d)", oField.GetWidth() );
812     }
813     else if( oField.GetType() == OFTDate )
814     {
815         strcpy( szFieldType, "date" );
816     }
817     else if( oField.GetType() == OFTTime )
818     {
819         strcpy( szFieldType, "time(7)" );
820     }
821     else if( oField.GetType() == OFTDateTime )
822     {
823         strcpy( szFieldType, "datetime" );
824     }
825     else if( oField.GetType() == OFTBinary )
826     {
827         strcpy( szFieldType, "image" );
828     }
829     else if( bApproxOK )
830     {
831         CPLError( CE_Warning, CPLE_NotSupported,
832                   "Can't create field %s with type %s on DB2 layers.  "
833                   "Creating as varchar.",
834                   oField.GetNameRef(),
835                   OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
836         strcpy( szFieldType, "varchar" );
837     }
838     else
839     {
840         CPLError( CE_Failure, CPLE_NotSupported,
841                   "Can't create field %s with type %s on DB2 layers.",
842                   oField.GetNameRef(),
843                   OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
844 
845         return OGRERR_FAILURE;
846     }
847 
848     /* -------------------------------------------------------------------- */
849     /*      Create the new field.                                           */
850     /* -------------------------------------------------------------------- */
851 
852     OGRDB2Statement oStmt( poDS->GetSession() );
853 
854     oStmt.Appendf( "ALTER TABLE %s.%s ADD COLUMN %s %s",
855                    pszSchemaName, pszTableName, oField.GetNameRef(),
856                    szFieldType);
857 
858     if( !oStmt.DB2Execute("OGR_DB2TableLayer::CreateField") )
859     {
860         CPLError( CE_Failure, CPLE_AppDefined,
861                   "Error creating field %s, %s", oField.GetNameRef(),
862                   poDS->GetSession()->GetLastError() );
863 
864         return OGRERR_FAILURE;
865     }
866 
867     /* -------------------------------------------------------------------- */
868     /*      Add the field to the OGRFeatureDefn.                            */
869     /* -------------------------------------------------------------------- */
870 
871     poFeatureDefn->AddFieldDefn( &oField );
872     return OGRERR_NONE;
873 }
874 
875 /************************************************************************/
876 /*                            ISetFeature()                             */
877 /*                                                                      */
878 /*     ISetFeature() is implemented by an UPDATE SQL command            */
879 /************************************************************************/
880 
ISetFeature(OGRFeature * poFeature)881 OGRErr OGRDB2TableLayer::ISetFeature( OGRFeature *poFeature )
882 
883 {
884     OGRErr              eErr = OGRERR_FAILURE;
885 
886     GetLayerDefn();
887 
888     if( nullptr == poFeature )
889     {
890         CPLError( CE_Failure, CPLE_AppDefined,
891                   "NULL pointer to OGRFeature passed to SetFeature()." );
892         return eErr;
893     }
894 
895     if( poFeature->GetFID() == OGRNullFID )
896     {
897         CPLError( CE_Failure, CPLE_AppDefined,
898                   "FID required on features given to SetFeature()." );
899         return eErr;
900     }
901 
902     if( !pszFIDColumn )
903     {
904         CPLError( CE_Failure, CPLE_AppDefined,
905                   "Unable to update features in tables without\n"
906                   "a recognised FID column.");
907         return eErr;
908     }
909 
910     ClearStatement();
911 
912     /* -------------------------------------------------------------------- */
913     /*      Form the UPDATE command.                                        */
914     /* -------------------------------------------------------------------- */
915     if (PrepareFeature(poFeature, 'U'))
916         return OGRERR_FAILURE;
917 
918     int nFieldCount = poFeatureDefn->GetFieldCount();
919     int nBindNum = 0;
920     void** papBindBuffer = (void**)CPLMalloc(sizeof(void*) * nFieldCount);
921 
922     /* Set the geometry */
923     OGRGeometry *poGeom = poFeature->GetGeometryRef();
924     char    *pszWKT = nullptr;
925 
926     if (poGeom != nullptr && pszGeomColumn != nullptr)
927     {
928         if( poGeom->exportToWkt( &pszWKT ) == OGRERR_NONE)
929         {
930             int nLen = (int) strlen(pszWKT);
931             if (m_poPrepStmt->DB2BindParameterIn(
932                         "OGRDB2TableLayer::UpdateFeature",
933                         (nBindNum + 1),
934                         SQL_C_CHAR,
935                         SQL_LONGVARCHAR,
936                         nLen,
937                         (void *)(pszWKT)))
938             {
939                 papBindBuffer[nBindNum] = pszWKT;
940                 nBindNum++;
941             } else {
942                 CPLDebug("OGRDB2TableLayer::UpdateFeature",
943                          "Bind parameter failed");
944                 FreeBindBuffer(nBindNum, papBindBuffer);
945                 return OGRERR_FAILURE;
946             }
947         }
948     }
949 
950     for( int i = 0; i < nFieldCount; i++ )
951     {
952 //        int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
953 //        CPLDebug("OGRDB2TableLayer::UpdateFeature",
954 //               "i: %d; nOGRFieldType: %d",
955 //                i, nOGRFieldType);
956 
957         if (BindFieldValue(m_poPrepStmt,
958                            poFeature, i,
959                            nBindNum, papBindBuffer) != OGRERR_NONE) {
960             CPLDebug("OGRDB2TableLayer::UpdateFeature",
961                      "Bind parameter failed");
962             FreeBindBuffer(nBindNum, papBindBuffer);
963             return OGRERR_FAILURE;
964         }
965         nBindNum++;
966     }
967 
968     /* -------------------------------------------------------------------- */
969     /*      Execute the update.                                             */
970     /* -------------------------------------------------------------------- */
971     if( !m_poPrepStmt->DB2Execute("OGR_DB2TableLayer::UpdateFeature") )
972     {
973         CPLError( CE_Failure, CPLE_AppDefined,
974                   "Error updating feature with FID:" CPL_FRMT_GIB ", %s",
975                   poFeature->GetFID(),
976                   poDS->GetSession()->GetLastError() );
977 
978         return OGRERR_FAILURE;
979     }
980 
981     FreeBindBuffer(nBindNum, papBindBuffer);
982 
983     return OGRERR_NONE;
984 }
985 
986 /************************************************************************/
987 /*                          DeleteFeature()                             */
988 /************************************************************************/
989 
DeleteFeature(GIntBig nFID)990 OGRErr OGRDB2TableLayer::DeleteFeature( GIntBig nFID )
991 
992 {
993     CPLDebug("OGR_DB2TableLayer::DeleteFeature",
994              " entering, nFID: " CPL_FRMT_GIB,nFID);
995     GetLayerDefn();
996 
997     if( pszFIDColumn == nullptr )
998     {
999         CPLError( CE_Failure, CPLE_AppDefined,
1000                   "DeleteFeature() without any FID column." );
1001         return OGRERR_FAILURE;
1002     }
1003 
1004     if( nFID == OGRNullFID )
1005     {
1006         CPLError( CE_Failure, CPLE_AppDefined,
1007                   "DeleteFeature() with unset FID fails." );
1008         return OGRERR_FAILURE;
1009     }
1010 
1011     ClearStatement();
1012 
1013     /* -------------------------------------------------------------------- */
1014     /*      Drop the record with this FID.                                  */
1015     /* -------------------------------------------------------------------- */
1016     OGRDB2Statement oStatement( poDS->GetSession() );
1017 
1018     oStatement.Appendf("DELETE FROM %s WHERE %s = " CPL_FRMT_GIB,
1019                        poFeatureDefn->GetName(), pszFIDColumn, nFID);
1020     if( !oStatement.DB2Execute("OGR_DB2TableLayer::DeleteFeature") )
1021     {
1022         CPLError( CE_Failure, CPLE_AppDefined,
1023                   "Attempt to delete feature with FID " CPL_FRMT_GIB
1024                   " failed. %s",
1025                   nFID, poDS->GetSession()->GetLastError() );
1026         return OGRERR_FAILURE;
1027     }
1028     return OGRERR_NONE;
1029 }
1030 
1031 /************************************************************************/
1032 /*                          isFieldTypeSupported()                      */
1033 /************************************************************************/
1034 
isFieldTypeSupported(OGRFieldType nFieldType)1035 OGRErr OGRDB2TableLayer::isFieldTypeSupported( OGRFieldType nFieldType )
1036 
1037 {
1038     switch(nFieldType) {
1039     case OFTInteger:
1040     case OFTReal:
1041     case OFTString:
1042     case OFTDateTime:
1043     case OFTInteger64:
1044         return OGRERR_NONE;
1045     default:
1046         return OGRERR_FAILURE;
1047     }
1048 }
1049 
1050 /************************************************************************/
1051 /*                          PrepareFeature()                            */
1052 /************************************************************************/
1053 
PrepareFeature(OGRFeature * poFeature,char cType)1054 OGRErr OGRDB2TableLayer::PrepareFeature( OGRFeature *poFeature, char cType )
1055 
1056 {
1057 // LATER - this defeats the point of prepared statements but need to find
1058 // some place to clean up to avoid reusing the wrong statement
1059     if (m_poPrepStmt) delete m_poPrepStmt;
1060     m_poPrepStmt =  new OGRDB2Statement( poDS->GetSession());
1061 
1062     char    *pszWKT = nullptr;
1063     CPLString osValues= " VALUES(";
1064     int nFieldCount = poFeatureDefn->GetFieldCount();
1065 
1066     if (cType == 'I')
1067         m_poPrepStmt->Appendf( "INSERT INTO %s.%s (",
1068                                pszSchemaName, pszTableName );
1069     else
1070         m_poPrepStmt->Appendf( "UPDATE %s.%s SET ",
1071                                pszSchemaName, pszTableName);
1072     int bNeedComma = FALSE;
1073     OGRGeometry *poGeom = poFeature->GetGeometryRef();
1074 
1075     if (poGeom != nullptr && pszGeomColumn != nullptr)
1076     {
1077         if( poGeom->exportToWkt( &pszWKT ) == OGRERR_NONE)
1078         {
1079             int nLen = (int) strlen(pszWKT);
1080             if (cType == 'I')
1081             {
1082                 m_poPrepStmt->Append( pszGeomColumn );
1083                 CPLString geomValue;
1084                 geomValue.Printf( "DB2GSE.ST_%s(CAST( ? AS CLOB(2M)),%d)",
1085                                   poGeom->getGeometryName(), nSRSId );
1086                 osValues.append(geomValue);
1087             } else {
1088                 m_poPrepStmt->Appendf( "%s = "
1089                                     "DB2GSE.ST_%s(CAST( ? AS CLOB(%d)),%d)",
1090                                     pszGeomColumn, poGeom->getGeometryName(),
1091                                     nLen, nSRSId );
1092             }
1093             bNeedComma = TRUE;
1094         }
1095     }
1096 
1097 // Explicitly add FID column and value if needed
1098     if( cType == 'I' && poFeature->GetFID() != OGRNullFID
1099             && pszFIDColumn != nullptr && cGenerated != 'A' )
1100     {
1101         if (bNeedComma)
1102         {
1103             m_poPrepStmt->Appendf( ", ");
1104             osValues.append(", ");
1105         }
1106         m_poPrepStmt->Appendf( "%s", pszFIDColumn );
1107         osValues.append("?");
1108         bNeedComma = TRUE;
1109     }
1110 
1111     for( int i = 0; i < nFieldCount; i++ )
1112     {
1113 
1114         if( !poFeature->IsFieldSetAndNotNull( i ) )
1115             continue;
1116 
1117         if (bNeedComma)
1118         {
1119             m_poPrepStmt->Appendf( ", ");
1120             osValues.append(", ");
1121         }
1122         bNeedComma = TRUE;
1123         if (cType == 'I') {
1124             m_poPrepStmt->Appendf( "%s",
1125                                 poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1126             osValues.append("?");
1127         } else {
1128             m_poPrepStmt->Appendf( "%s = ?",
1129                                 poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1130         }
1131     }
1132     if (cType == 'I') {
1133         m_poPrepStmt->Appendf( ") %s )", osValues.c_str() );
1134     } else {
1135         /* Add the WHERE clause */
1136         m_poPrepStmt->Appendf( " WHERE (%s) = " CPL_FRMT_GIB, pszFIDColumn,
1137                                poFeature->GetFID());
1138     }
1139     if (!m_poPrepStmt->DB2Prepare("OGR_DB2TableLayer::PrepareFeature"))
1140     {
1141         CPLError( CE_Failure, CPLE_AppDefined,
1142                   "PREPARE command for feature failed. %s",
1143                   poDS->GetSession()->GetLastError() );
1144         return OGRERR_FAILURE;
1145     }
1146 
1147     return OGRERR_NONE;
1148 }
1149 
1150 /************************************************************************/
1151 /*                          ICreateFeature()                            */
1152 /************************************************************************/
1153 
ICreateFeature(OGRFeature * poFeature)1154 OGRErr OGRDB2TableLayer::ICreateFeature( OGRFeature *poFeature )
1155 {
1156     GetLayerDefn();
1157 
1158     if( nullptr == poFeature )
1159     {
1160         CPLError( CE_Failure, CPLE_AppDefined,
1161                   "NULL pointer to OGRFeature passed to CreateFeature()." );
1162         return OGRERR_FAILURE;
1163     }
1164 
1165     if (PrepareFeature(poFeature, 'I'))
1166         return OGRERR_FAILURE;
1167 
1168     char    *pszWKT = nullptr;
1169     int nFieldCount = poFeatureDefn->GetFieldCount();
1170     int nBindNum = 0;
1171     void** papBindBuffer = (void**)CPLMalloc(sizeof(void*)
1172                                                 * (nFieldCount + 1));
1173     OGRGeometry *poGeom = poFeature->GetGeometryRef();
1174 
1175     if (poGeom != nullptr && pszGeomColumn != nullptr)
1176     {
1177         if( poGeom->exportToWkt( &pszWKT ) == OGRERR_NONE)
1178         {
1179             int nLen = (int) strlen(pszWKT);
1180             if (m_poPrepStmt->DB2BindParameterIn(
1181                         "OGRDB2TableLayer::ICreateFeature",
1182                         (nBindNum + 1),
1183                         SQL_C_CHAR,
1184                         SQL_LONGVARCHAR,
1185                         nLen,
1186                         (void *)(pszWKT)))
1187             {
1188                 papBindBuffer[nBindNum] = pszWKT;
1189                 nBindNum++;
1190             }
1191             else
1192             {
1193                 CPLDebug("OGRDB2TableLayer::ICreateFeature",
1194                          "Bind parameter failed");
1195                 FreeBindBuffer(nBindNum, papBindBuffer);
1196                 return OGRERR_FAILURE;
1197             }
1198         }
1199     }
1200 
1201 // Explicitly add FID column and value if needed
1202     if( poFeature->GetFID() != OGRNullFID
1203             && pszFIDColumn != nullptr && cGenerated != 'A' )
1204     {
1205         GIntBig nFID = poFeature->GetFID();
1206         if (m_poPrepStmt->DB2BindParameterIn(
1207                     "OGRDB2TableLayer::ICreateFeature",
1208                     (nBindNum + 1),
1209                     SQL_C_SBIGINT,
1210                     SQL_BIGINT,
1211                     sizeof(GIntBig),
1212                     (void *)(&nFID)))
1213         {
1214             papBindBuffer[nBindNum] = nullptr;
1215             nBindNum++;
1216         }
1217         else
1218         {
1219             CPLDebug("OGRDB2TableLayer::ICreateFeature",
1220                      "Bind parameter failed");
1221             FreeBindBuffer(nBindNum, papBindBuffer);
1222             return OGRERR_FAILURE;
1223         }
1224     }
1225 
1226     for( int i = 0; i < nFieldCount; i++ )
1227     {
1228 
1229         if( !poFeature->IsFieldSetAndNotNull( i ) )
1230             continue;
1231 
1232 //        int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
1233 //        CPLDebug("OGRDB2TableLayer::ICreateFeature",
1234 //               "i: %d; nOGRFieldType: %d",
1235 //                i, nOGRFieldType);
1236 
1237         if (BindFieldValue(m_poPrepStmt,
1238                            poFeature, i,
1239                            nBindNum, papBindBuffer) != OGRERR_NONE) {
1240             CPLDebug("OGRDB2TableLayer::ICreateFeature",
1241                      "Bind parameter failed");
1242             FreeBindBuffer(nBindNum, papBindBuffer);
1243             return OGRERR_FAILURE;
1244         }
1245         nBindNum++;
1246     }
1247 
1248     poDS->getDTime();
1249     /* -------------------------------------------------------------------- */
1250     /*      Execute the insert.                                             */
1251     /* -------------------------------------------------------------------- */
1252 
1253     if (!m_poPrepStmt->DB2Execute("OGR_DB2TableLayer::ICreateFeature"))
1254     {
1255         CPLError( CE_Failure, CPLE_AppDefined,
1256                   "INSERT command for new feature failed. %s",
1257                   poDS->GetSession()->GetLastError() );
1258         FreeBindBuffer(nBindNum, papBindBuffer);
1259         return OGRERR_FAILURE;
1260     }
1261     poDS->getDTime();
1262 
1263     if( bIsIdentityFid) {
1264         GIntBig oldFID = poFeature->GetFID();
1265         OGRDB2Statement oStatement2( poDS->GetSession() );
1266         oStatement2.Append( "select IDENTITY_VAL_LOCAL() AS IDENTITY "
1267                             "FROM SYSIBM.SYSDUMMY1");
1268         if( oStatement2.DB2Execute("OGR_DB2TableLayer::ICreateFeature")
1269                 && oStatement2.Fetch() )
1270         {
1271             poFeature->SetFID( atoi(oStatement2.GetColData( 0 ) ));
1272 
1273             if ( oStatement2.GetColData( 0 ) )
1274             {
1275                 poFeature->SetFID( atoi(oStatement2.GetColData( 0 ) ));
1276             }
1277         }
1278         CPLDebug("OGR_DB2TableLayer::ICreateFeature","Old FID: " CPL_FRMT_GIB
1279                  "; New FID: " CPL_FRMT_GIB, oldFID, poFeature->GetFID());
1280     }
1281 
1282     FreeBindBuffer(nBindNum, papBindBuffer);
1283 
1284     return OGRERR_NONE;
1285 }
1286 
1287 /************************************************************************/
1288 /*                          FreeBindBuffer()                            */
1289 /************************************************************************/
1290 
FreeBindBuffer(int nBindNum,void ** papBindBuffer)1291 void OGRDB2TableLayer::FreeBindBuffer(int nBindNum, void **papBindBuffer)
1292 {
1293     for( int i = 0; i < nBindNum; i++ ) {
1294         if (papBindBuffer[i] ) CPLFree(papBindBuffer[i]); // only free if set
1295     };
1296     CPLFree(papBindBuffer);
1297 }
1298 
1299 /************************************************************************/
1300 /*                          BindFieldValue()                            */
1301 /*                                                                      */
1302 /* Used by CreateFeature() and SetFeature() to bind a                   */
1303 /* non-empty field value                                                */
1304 /************************************************************************/
1305 
BindFieldValue(OGRDB2Statement *,OGRFeature * poFeature,int i,int nBindNum,void ** papBindBuffer)1306 OGRErr OGRDB2TableLayer::BindFieldValue(OGRDB2Statement * /*poStatement*/,
1307                                         OGRFeature* poFeature, int i,
1308                                         int nBindNum, void **papBindBuffer)
1309 {
1310     int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
1311 
1312     int nLen = 0;
1313     void * pValuePointer = nullptr;
1314     int nValueType = 0;
1315     int nParameterType = 0;
1316 
1317     if( nOGRFieldType == OFTString ) {
1318         const char* stringValue = poFeature->GetFieldAsString(i);
1319         papBindBuffer[nBindNum] = nullptr; // Don't free
1320         nLen = (int) strlen(stringValue);
1321         pValuePointer = (void *) stringValue;
1322         nValueType = SQL_C_CHAR;
1323         nParameterType = SQL_VARCHAR;
1324     }
1325 
1326     if ( nOGRFieldType == OFTReal ) {
1327         double *pnRealValue = (double *)CPLMalloc(sizeof(double));
1328         papBindBuffer[nBindNum] = pnRealValue;
1329         *pnRealValue = poFeature->GetFieldAsInteger(i);
1330         nLen = sizeof(double);
1331         pValuePointer = (void *) pnRealValue;
1332         nValueType = SQL_C_DOUBLE;
1333         nParameterType = SQL_DOUBLE;
1334     }
1335 
1336     if ( nOGRFieldType == OFTInteger ) {
1337         int *pnIntValue = (int *)CPLMalloc(sizeof(int));
1338         papBindBuffer[nBindNum] = pnIntValue;
1339         *pnIntValue = poFeature->GetFieldAsInteger(i);
1340         nLen = sizeof(int);
1341         pValuePointer = (void *) pnIntValue;
1342         nValueType = SQL_C_SLONG;
1343         nParameterType = SQL_INTEGER;
1344     }
1345 
1346     if ( nOGRFieldType == OFTInteger64 ) {
1347         GIntBig *pnLongValue = (GIntBig *)CPLMalloc(sizeof(GIntBig));
1348         papBindBuffer[nBindNum] = pnLongValue;
1349         *pnLongValue = poFeature->GetFieldAsInteger64(i);
1350         nLen = sizeof(GIntBig);
1351         pValuePointer = (void *) pnLongValue;
1352         nValueType = SQL_C_SBIGINT;
1353         nParameterType = SQL_BIGINT;
1354     }
1355 
1356     if (pValuePointer) {
1357         if (!m_poPrepStmt->DB2BindParameterIn(
1358                     "OGRDB2TableLayer::BindFieldValue",
1359                     (nBindNum + 1),
1360                     nValueType,
1361                     nParameterType,
1362                     nLen,
1363                     pValuePointer))
1364         {
1365             CPLDebug("OGRDB2TableLayer::BindFieldValue",
1366                      "Bind parameter failed");
1367             return OGRERR_FAILURE;
1368         }
1369     }
1370     return OGRERR_NONE;
1371 }
1372 
1373 #ifdef notdef
1374 /************************************************************************/
1375 /*                     CreateSpatialIndexIfNecessary()                  */
1376 /************************************************************************/
1377 
CreateSpatialIndexIfNecessary()1378 void OGRDB2TableLayer::CreateSpatialIndexIfNecessary()
1379 {
1380     if( bDeferredSpatialIndexCreation )
1381     {
1382         CreateSpatialIndex();
1383     }
1384 }
1385 #endif
1386 
1387 /************************************************************************/
1388 /*                      RunDeferredCreationIfNecessary()                */
1389 /************************************************************************/
1390 
RunDeferredCreationIfNecessary()1391 OGRErr OGRDB2TableLayer::RunDeferredCreationIfNecessary()
1392 {
1393     CPLDebug("OGRDB2TableLayer::RunDeferredCreationIfNecessary","NO-OP");
1394 #ifdef LATER
1395     if( !m_bDeferredCreation )
1396         return OGRERR_NONE;
1397     m_bDeferredCreation = FALSE;
1398 
1399     const char* pszLayerName = m_poFeatureDefn->GetName();
1400     OGRwkbGeometryType eGType = GetGeomType();
1401 
1402     int bIsSpatial = (eGType != wkbNone);
1403 
1404  /* Requirement 25: The geometry_type_name value in a gpkg_geometry_columns */
1405  /* row SHALL be one of the uppercase geometry type names specified in */
1406  /* Geometry Types (Normative). */
1407     const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
1408 
1409     /* Create the table! */
1410     char *pszSQL = NULL;
1411     CPLString osCommand;
1412 
1413     pszSQL = sqlite3_mprintf(
1414                  "CREATE TABLE \"%s\" ( "
1415                  "\"%s\" INTEGER PRIMARY KEY AUTOINCREMENT",
1416                  pszLayerName, m_pszFidColumn);
1417     osCommand += pszSQL;
1418     sqlite3_free(pszSQL);
1419 
1420     if( GetGeomType() != wkbNone )
1421     {
1422         pszSQL = sqlite3_mprintf(", '%q' %s",
1423                                  GetGeometryColumn(), pszGeometryType);
1424         osCommand += pszSQL;
1425         sqlite3_free(pszSQL);
1426         if( !m_poFeatureDefn->GetGeomFieldDefn(0)->IsNullable() )
1427         {
1428             osCommand += " NOT NULL";
1429         }
1430     }
1431 
1432     for(int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++ )
1433     {
1434         if( i == m_iFIDAsRegularColumnIndex )
1435             continue;
1436         OGRFieldDefn* poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
1437         pszSQL = sqlite3_mprintf(", '%q' %s",
1438                                  poFieldDefn->GetNameRef(),
1439                                  GPkgFieldFromOGR(poFieldDefn->GetType(),
1440                                          poFieldDefn->GetSubType(),
1441                                          poFieldDefn->GetWidth()));
1442         osCommand += pszSQL;
1443         sqlite3_free(pszSQL);
1444         if( !poFieldDefn->IsNullable() )
1445         {
1446             osCommand += " NOT NULL";
1447         }
1448         const char* pszDefault = poFieldDefn->GetDefault();
1449         if( pszDefault != NULL &&
1450                 (!poFieldDefn->IsDefaultDriverSpecific() ||
1451                  (pszDefault[0] == '('
1452                  && pszDefault[strlen(pszDefault)-1] == ')'
1453                  && (STARTS_WITH_CI(pszDefault+1, "strftime")
1454                  || STARTS_WITH_CI(pszDefault+1, " strftime")))) )
1455         {
1456             osCommand += " DEFAULT ";
1457             OGRField sField;
1458             if( poFieldDefn->GetType() == OFTDateTime &&
1459                     OGRParseDate(pszDefault, &sField, 0) )
1460             {
1461                 char* pszXML = OGRGetXMLDateTime(&sField);
1462                 osCommand += pszXML;
1463                 CPLFree(pszXML);
1464             }
1465 /* Make sure CURRENT_TIMESTAMP is translated into appropriate format */
1466 /* for GeoPackage */
1467             else if( poFieldDefn->GetType() == OFTDateTime &&
1468                      EQUAL(pszDefault, "CURRENT_TIMESTAMP") )
1469             {
1470                 osCommand += "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))";
1471             }
1472             else
1473             {
1474                 osCommand += poFieldDefn->GetDefault();
1475             }
1476         }
1477     }
1478 
1479     osCommand += ")";
1480 
1481     OGRErr err = SQLCommand(m_poDS->GetDB(), osCommand.c_str());
1482     if ( OGRERR_NONE != err )
1483         return OGRERR_FAILURE;
1484 
1485     /* Update gpkg_contents with the table info */
1486     if ( bIsSpatial )
1487         err = RegisterGeometryColumn();
1488     else
1489         err = m_poDS->CreateGDALAspatialExtension();
1490 
1491     if ( err != OGRERR_NONE )
1492         return OGRERR_FAILURE;
1493 
1494     const char* pszIdentifier = GetMetadataItem("IDENTIFIER");
1495     if( pszIdentifier == NULL )
1496         pszIdentifier = pszLayerName;
1497     const char* pszDescription = GetMetadataItem("DESCRIPTION");
1498     if( pszDescription == NULL )
1499         pszDescription = "";
1500     pszSQL = sqlite3_mprintf(
1501                  "INSERT INTO gpkg_contents "
1502                  "(table_name,data_type,identifier,description,"
1503                  "last_change,srs_id)"
1504                  " VALUES "
1505                  "('%q','%q','%q','%q',strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ',"
1506                  "CURRENT_TIMESTAMP),%d)",
1507                  pszLayerName, (bIsSpatial ? "features": "aspatial"),
1508                  pszIdentifier, pszDescription, m_iSrs);
1509 
1510     err = SQLCommand(m_poDS->GetDB(), pszSQL);
1511     sqlite3_free(pszSQL);
1512     if ( err != OGRERR_NONE )
1513         return OGRERR_FAILURE;
1514 
1515     ResetReading();
1516 #endif
1517     return OGRERR_NONE;
1518 }
1519