1 /******************************************************************************
2  *
3  * Project:  Oracle Spatial Driver
4  * Purpose:  Implementation of the OGROCITableLayer class.  This class provides
5  *           layer semantics on a table, but utilizing a lot of machinery from
6  *           the OGROCILayer base class.
7  * Author:   Frank Warmerdam, warmerdam@pobox.com
8  *
9  ******************************************************************************
10  * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "ogr_oci.h"
32 #include "cpl_conv.h"
33 #include "cpl_string.h"
34 
35 CPL_CVSID("$Id: ogrocitablelayer.cpp fa752ad6eabafaf630a704e1892a9d837d683cb3 2021-03-06 17:04:38 +0100 Even Rouault $")
36 
37 static int nDiscarded = 0;
38 static int nHits = 0;
39 
40 #define HSI_UNKNOWN  -2
41 
42 /************************************************************************/
43 /*                          OGROCITableLayer()                          */
44 /************************************************************************/
45 
OGROCITableLayer(OGROCIDataSource * poDSIn,const char * pszTableName,OGRwkbGeometryType eGType,int nSRIDIn,int bUpdate,int bNewLayerIn)46 OGROCITableLayer::OGROCITableLayer( OGROCIDataSource *poDSIn,
47                                     const char * pszTableName, OGRwkbGeometryType eGType,
48                                     int nSRIDIn, int bUpdate, int bNewLayerIn )
49 
50 {
51     poDS = poDSIn;
52     bExtentUpdated = false;
53 
54     pszQuery = nullptr;
55     pszWHERE = CPLStrdup( "" );
56     pszQueryStatement = nullptr;
57 
58     bUpdateAccess = bUpdate;
59     bNewLayer = bNewLayerIn;
60 
61     iNextShapeId = 0;
62     iNextFIDToWrite = -1;
63 
64     bValidTable = FALSE;
65     if( bNewLayerIn )
66         bHaveSpatialIndex = FALSE;
67     else
68         bHaveSpatialIndex = HSI_UNKNOWN;
69 
70     poFeatureDefn = ReadTableDefinition( pszTableName );
71     if( eGType != wkbUnknown && poFeatureDefn->GetGeomFieldCount() > 0 )
72         poFeatureDefn->GetGeomFieldDefn(0)->SetType(eGType);
73     SetDescription( poFeatureDefn->GetName() );
74 
75     nSRID = nSRIDIn;
76     if( nSRID == -1 )
77         nSRID = LookupTableSRID();
78 
79     poSRS = poDSIn->FetchSRS( nSRID );
80     if( poSRS != nullptr )
81         poSRS->Reference();
82 
83     hOrdVARRAY = nullptr;
84     hElemInfoVARRAY = nullptr;
85 
86     poBoundStatement = nullptr;
87 
88     nWriteCacheMax = 0;
89     nWriteCacheUsed = 0;
90     pasWriteGeoms = nullptr;
91     papsWriteGeomMap = nullptr;
92     pasWriteGeomInd = nullptr;
93     papsWriteGeomIndMap = nullptr;
94 
95     papWriteFields = nullptr;
96     papaeWriteFieldInd = nullptr;
97 
98     panWriteFIDs = nullptr;
99 
100     nDefaultStringSize = 4000;
101 
102     OGROCITableLayer::ResetReading();
103 }
104 
105 /************************************************************************/
106 /*                         ~OGROCITableLayer()                          */
107 /************************************************************************/
108 
~OGROCITableLayer()109 OGROCITableLayer::~OGROCITableLayer()
110 
111 {
112     int   i;
113 
114     OGROCITableLayer::SyncToDisk();
115 
116     CPLFree( panWriteFIDs );
117     if( papWriteFields != nullptr )
118     {
119         for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
120         {
121             CPLFree( papWriteFields[i] );
122             CPLFree( papaeWriteFieldInd[i] );
123         }
124     }
125 
126     CPLFree( papWriteFields );
127     CPLFree( papaeWriteFieldInd );
128 
129     if( poBoundStatement != nullptr )
130         delete poBoundStatement;
131 
132     CPLFree( pasWriteGeomInd );
133     CPLFree( papsWriteGeomIndMap );
134 
135     CPLFree( papsWriteGeomMap );
136     CPLFree( pasWriteGeoms );
137 
138     CPLFree( pszQuery );
139     CPLFree( pszWHERE );
140 
141     if( poSRS != nullptr && poSRS->Dereference() == 0 )
142         delete poSRS;
143 }
144 
145 /************************************************************************/
146 /*                        ReadTableDefinition()                         */
147 /*                                                                      */
148 /*      Build a schema from the named table.  Done by querying the      */
149 /*      catalog.                                                        */
150 /************************************************************************/
151 
ReadTableDefinition(const char * pszTable)152 OGRFeatureDefn *OGROCITableLayer::ReadTableDefinition( const char * pszTable )
153 
154 {
155     OGROCISession      *poSession = poDS->GetSession();
156     sword               nStatus;
157 
158     CPLString osUnquotedTableName;
159     CPLString osQuotedTableName;
160 
161 /* -------------------------------------------------------------------- */
162 /*      Split out the owner if available.                               */
163 /* -------------------------------------------------------------------- */
164     if( strstr(pszTable,".") != nullptr )
165     {
166         osTableName = strstr(pszTable,".") + 1;
167         osOwner.assign( pszTable, strlen(pszTable)-osTableName.size() - 1 );
168         osUnquotedTableName.Printf( "%s.%s", osOwner.c_str(), osTableName.c_str() );
169         osQuotedTableName.Printf( "\"%s\".\"%s\"", osOwner.c_str(), osTableName.c_str() );
170     }
171     else
172     {
173         osTableName = pszTable;
174         osOwner = "";
175         osUnquotedTableName.Printf( "%s", pszTable );
176         osQuotedTableName.Printf( "\"%s\"", pszTable );
177     }
178 
179     OGRFeatureDefn *poDefn = new OGRFeatureDefn( osUnquotedTableName.c_str() );
180 
181     poDefn->Reference();
182 
183 /* -------------------------------------------------------------------- */
184 /*      Do a DescribeAll on the table.                                  */
185 /* -------------------------------------------------------------------- */
186     OCIParam *hAttrParam = nullptr;
187     OCIParam *hAttrList = nullptr;
188 
189     // Table name unquoted
190 
191     nStatus =
192         OCIDescribeAny( poSession->hSvcCtx, poSession->hError,
193                         (dvoid *) osUnquotedTableName.c_str(),
194                         static_cast<ub4>(osUnquotedTableName.length()), OCI_OTYPE_NAME,
195                         OCI_DEFAULT, OCI_PTYPE_TABLE, poSession->hDescribe );
196 
197     if( poSession->Failed( nStatus, "OCIDescribeAny" ) )
198     {
199         CPLErrorReset();
200 
201         // View name unquoted
202 
203         nStatus =
204             OCIDescribeAny(poSession->hSvcCtx, poSession->hError,
205                            (dvoid *) osUnquotedTableName.c_str(),
206                            static_cast<ub4>(osUnquotedTableName.length()), OCI_OTYPE_NAME,
207                            OCI_DEFAULT, OCI_PTYPE_VIEW, poSession->hDescribe );
208 
209         if( poSession->Failed( nStatus, "OCIDescribeAny" ) )
210         {
211             CPLErrorReset();
212 
213             // Table name quoted
214 
215             nStatus =
216                 OCIDescribeAny( poSession->hSvcCtx, poSession->hError,
217                                 (dvoid *) osQuotedTableName.c_str(),
218                                 static_cast<ub4>(osQuotedTableName.length()), OCI_OTYPE_NAME,
219                                 OCI_DEFAULT, OCI_PTYPE_TABLE, poSession->hDescribe );
220 
221             if( poSession->Failed( nStatus, "OCIDescribeAny" ) )
222             {
223                 CPLErrorReset();
224 
225                 // View name quoted
226 
227                 nStatus =
228                     OCIDescribeAny(poSession->hSvcCtx, poSession->hError,
229                                    (dvoid *) osQuotedTableName.c_str(),
230                                    static_cast<ub4>(osQuotedTableName.length()), OCI_OTYPE_NAME,
231                                    OCI_DEFAULT, OCI_PTYPE_VIEW, poSession->hDescribe );
232 
233                 if( poSession->Failed( nStatus, "OCIDescribeAny" ) )
234                     return poDefn;
235             }
236         }
237     }
238 
239     if( poSession->Failed(
240         OCIAttrGet( poSession->hDescribe, OCI_HTYPE_DESCRIBE,
241                     &hAttrParam, nullptr, OCI_ATTR_PARAM, poSession->hError ),
242         "OCIAttrGet(ATTR_PARAM)") )
243         return poDefn;
244 
245     if( poSession->Failed(
246         OCIAttrGet( hAttrParam, OCI_DTYPE_PARAM, &hAttrList, nullptr,
247                     OCI_ATTR_LIST_COLUMNS, poSession->hError ),
248         "OCIAttrGet(ATTR_LIST_COLUMNS)" ) )
249         return poDefn;
250 
251 /* -------------------------------------------------------------------- */
252 /*      What is the name of the column to use as FID?  This defaults    */
253 /*      to OGR_FID but we allow it to be overridden by a config         */
254 /*      variable.  Ideally we would identify a column that is a         */
255 /*      primary key and use that, but I'm not yet sure how to           */
256 /*      accomplish that.                                                */
257 /* -------------------------------------------------------------------- */
258     const char *pszExpectedFIDName =
259         CPLGetConfigOption( "OCI_FID", "OGR_FID" );
260     int bGeomFieldNullable = FALSE;
261 
262 /* -------------------------------------------------------------------- */
263 /*      Parse the returned table information.                           */
264 /* -------------------------------------------------------------------- */
265     for( int iRawFld = 0; true; iRawFld++ )
266     {
267         OGRFieldDefn    oField( "", OFTString);
268         OCIParam     *hParamDesc;
269         ub2          nOCIType;
270         ub4          nOCILen;
271 
272         nStatus = OCIParamGet( hAttrList, OCI_DTYPE_PARAM,
273                                poSession->hError, (dvoid**)&hParamDesc,
274                                (ub4) iRawFld+1 );
275         if( nStatus != OCI_SUCCESS )
276             break;
277 
278         if( poSession->GetParamInfo( hParamDesc, &oField, &nOCIType, &nOCILen )
279             != CE_None )
280             return poDefn;
281 
282         if( oField.GetType() == OFTBinary )
283         {
284             if( nOCIType == 108 && pszGeomName == nullptr )
285             {
286                 CPLFree( pszGeomName );
287                 pszGeomName = CPLStrdup( oField.GetNameRef() );
288                 iGeomColumn = iRawFld;
289                 bGeomFieldNullable = oField.IsNullable();
290             }
291             continue;
292         }
293 
294         if( EQUAL(oField.GetNameRef(),pszExpectedFIDName)
295             && (oField.GetType() == OFTInteger ||
296                 oField.GetType() == OFTInteger64) )
297         {
298             pszFIDName = CPLStrdup(oField.GetNameRef());
299             continue;
300         }
301 
302         poDefn->AddFieldDefn( &oField );
303     }
304 
305     OGROCIStatement defaultValuesStatement( poSession );
306 
307     const char* pszDefaultValueSQL =
308         "SELECT COLUMN_NAME, DATA_DEFAULT\n"
309         "FROM user_tab_columns\n"
310         "WHERE DATA_DEFAULT IS NOT NULL AND TABLE_NAME = UPPER(:table_name)";
311 
312     defaultValuesStatement.Prepare(pszDefaultValueSQL);
313     defaultValuesStatement.BindString(":table_name", pszTable);
314 
315     if( defaultValuesStatement.Execute( nullptr ) == CE_None )
316     {
317         char **papszRow;
318 
319         while( (papszRow = defaultValuesStatement.SimpleFetchRow()) != nullptr )
320         {
321             const char* pszColName = papszRow[0];
322             const char* pszDefault = papszRow[1];
323             int nIdx = poDefn->GetFieldIndex(pszColName);
324             if( nIdx >= 0 )
325                 poDefn->GetFieldDefn(nIdx)->SetDefault(pszDefault);
326         }
327     }
328 
329     if( EQUAL(pszExpectedFIDName, "OGR_FID") && pszFIDName )
330     {
331         for(int i=0;i<poDefn->GetFieldCount();i++)
332         {
333             // This is presumably a Integer since we always create Integer64 with a
334             // defined precision
335             if( poDefn->GetFieldDefn(i)->GetType() == OFTInteger64 &&
336                 poDefn->GetFieldDefn(i)->GetWidth() == 0 )
337             {
338                 poDefn->GetFieldDefn(i)->SetType(OFTInteger);
339             }
340         }
341     }
342 
343     /* -------------------------------------------------------------------- */
344     /*      Identify Geometry dimension                                     */
345     /* -------------------------------------------------------------------- */
346 
347     if( pszGeomName != nullptr && strlen(pszGeomName) > 0 )
348     {
349         OGROCIStatement oDimStatement( poSession );
350         char **papszResult;
351         int iDim = -1;
352 
353         if( osOwner != "" )
354         {
355             const char* pszDimCmdA =
356                 "SELECT COUNT(*)\n"
357                 "FROM ALL_SDO_GEOM_METADATA u, TABLE(u.diminfo) t\n"
358                 "WHERE u.table_name = :table_name\n"
359                 "  AND u.column_name = :geometry_name\n"
360                 "  AND u.owner = :table_owner";
361 
362             oDimStatement.Prepare( pszDimCmdA );
363             oDimStatement.BindString( ":table_name", osTableName.c_str() );
364             oDimStatement.BindString( ":geometry_name", pszGeomName );
365             oDimStatement.BindString( ":table_owner", osOwner.c_str() );
366         }
367         else
368         {
369             const char* pszDimCmdB =
370                 "SELECT COUNT(*)\n"
371                 "FROM USER_SDO_GEOM_METADATA u, TABLE(u.diminfo) t\n"
372                 "WHERE u.table_name = :table_name\n"
373                 "  AND u.column_name = :geometry_name";
374 
375             oDimStatement.Prepare( pszDimCmdB );
376             oDimStatement.BindString( ":table_name", osTableName.c_str() );
377             oDimStatement.BindString( ":geometry_name", pszGeomName );
378         }
379         oDimStatement.Execute( nullptr );
380 
381         papszResult = oDimStatement.SimpleFetchRow();
382 
383         if( CSLCount(papszResult) < 1 )
384         {
385             OGROCIStatement oDimStatement2( poSession );
386             char **papszResult2;
387 
388             CPLErrorReset();
389 
390             if( osOwner != "" )
391             {
392                 const char* pszDimCmd2A =
393                     "select m.sdo_index_dims\n"
394                     "from   all_sdo_index_metadata m, all_sdo_index_info i\n"
395                     "where  i.index_name = m.sdo_index_name\n"
396                     "   and i.sdo_index_owner = m.sdo_index_owner\n"
397                     "   and i.sdo_index_owner = upper(:table_owner)\n"
398                     "   and i.table_name = upper(:table_name)";
399 
400                 oDimStatement2.Prepare( pszDimCmd2A );
401                 oDimStatement2.BindString( ":table_owner", osOwner.c_str());
402                 oDimStatement2.BindString( ":table_name", osTableName.c_str());
403             }
404             else
405             {
406                 const char* pszDimCmd2B =
407                     "select m.sdo_index_dims\n"
408                     "from   user_sdo_index_metadata m, user_sdo_index_info i\n"
409                     "where  i.index_name = m.sdo_index_name\n"
410                     "   and i.table_name = upper(:table_name)";
411 
412                 oDimStatement2.Prepare( pszDimCmd2B );
413                 oDimStatement2.BindString( ":table_name", osTableName.c_str());
414             }
415             oDimStatement2.Execute( nullptr );
416 
417             papszResult2 = oDimStatement2.SimpleFetchRow();
418 
419             if( CSLCount( papszResult2 ) > 0 )
420             {
421                 iDim = atoi( papszResult2[0] );
422             }
423             else
424             {
425                 // we want to clear any errors to avoid confusing the application.
426                 CPLErrorReset();
427             }
428         }
429         else
430         {
431             iDim = atoi( papszResult[0] );
432         }
433 
434         if( iDim > 0 )
435         {
436             SetDimension( iDim );
437         }
438         else
439         {
440             CPLDebug( "OCI", "get dim based of existing data or index failed." );
441         }
442 
443         {
444             OGROCIStatement oDimStatement2( poSession );
445             char **papszResult2;
446 
447             CPLErrorReset();
448             if( osOwner != "" )
449             {
450                 const char* pszLayerTypeCmdA =
451                     "select m.SDO_LAYER_GTYPE "
452                     "from all_sdo_index_metadata m, all_sdo_index_info i "
453                     "where i.index_name = m.sdo_index_name "
454                     "and i.sdo_index_owner = m.sdo_index_owner "
455                     "and i.sdo_index_owner = upper(:table_owner) "
456                     "and i.table_name = upper(:table_name)";
457 
458                 oDimStatement2.Prepare( pszLayerTypeCmdA );
459                 oDimStatement2.BindString( ":table_owner", osOwner.c_str() );
460                 oDimStatement2.BindString( ":table_name", osTableName.c_str() );
461             }
462             else
463             {
464                 const char* pszLayerTypeCmdB =
465                     "select m.SDO_LAYER_GTYPE "
466                     "from user_sdo_index_metadata m, user_sdo_index_info i "
467                     "where i.index_name = m.sdo_index_name "
468                     "and i.table_name = upper(:table_name)";
469                 oDimStatement2.Prepare( pszLayerTypeCmdB );
470                 oDimStatement2.BindString( ":table_name", osTableName.c_str() );
471             }
472 
473             oDimStatement2.Execute( nullptr );
474 
475             papszResult2 = oDimStatement2.SimpleFetchRow();
476 
477             if( CSLCount( papszResult2 ) > 0 )
478             {
479                 const char* pszLayerGType = papszResult2[0];
480                 OGRwkbGeometryType eGeomType = wkbUnknown;
481                 if( EQUAL(pszLayerGType, "POINT") )
482                     eGeomType = wkbPoint;
483                 else if( EQUAL(pszLayerGType, "LINE") )
484                     eGeomType = wkbLineString;
485                 else if( EQUAL(pszLayerGType, "POLYGON") )
486                     eGeomType = wkbPolygon;
487                 else if( EQUAL(pszLayerGType, "MULTIPOINT") )
488                     eGeomType = wkbMultiPoint;
489                 else if( EQUAL(pszLayerGType, "MULTILINE") )
490                     eGeomType = wkbMultiLineString;
491                 else if( EQUAL(pszLayerGType, "MULTIPOLYGON") )
492                     eGeomType = wkbMultiPolygon;
493                 else if( !EQUAL(pszLayerGType, "COLLECTION") )
494                     CPLDebug("OCI", "LAYER_GTYPE = %s", pszLayerGType );
495                 if( iDim == 3 )
496                     eGeomType = wkbSetZ(eGeomType);
497                 poDefn->GetGeomFieldDefn(0)->SetType( eGeomType );
498                 poDefn->GetGeomFieldDefn(0)->SetNullable( bGeomFieldNullable );
499             }
500             else
501             {
502                 // we want to clear any errors to avoid confusing the application.
503                 CPLErrorReset();
504             }
505         }
506     }
507     else
508     {
509         poDefn->SetGeomType(wkbNone);
510     }
511 
512     bValidTable = TRUE;
513 
514     return poDefn;
515 }
516 
517 /************************************************************************/
518 /*                          SetSpatialFilter()                          */
519 /************************************************************************/
520 
SetSpatialFilter(OGRGeometry * poGeomIn)521 void OGROCITableLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
522 
523 {
524     if( !InstallFilter( poGeomIn ) )
525         return;
526 
527     BuildWhere();
528 
529     ResetReading();
530 }
531 
532 /************************************************************************/
533 /*                        TestForSpatialIndex()                         */
534 /************************************************************************/
535 
TestForSpatialIndex(const char * pszSpatWHERE)536 void OGROCITableLayer::TestForSpatialIndex( const char *pszSpatWHERE )
537 
538 {
539     OGROCIStringBuf oTestCmd;
540     OGROCIStatement oTestStatement( poDS->GetSession() );
541 
542     oTestCmd.Append( "SELECT COUNT(*) FROM " );
543     oTestCmd.Append( poFeatureDefn->GetName() );
544     oTestCmd.Append( pszSpatWHERE );
545 
546     if( oTestStatement.Execute( oTestCmd.GetString() ) != CE_None )
547         bHaveSpatialIndex = FALSE;
548     else
549         bHaveSpatialIndex = TRUE;
550 }
551 
552 /************************************************************************/
553 /*                             BuildWhere()                             */
554 /*                                                                      */
555 /*      Build the WHERE statement appropriate to the current set of     */
556 /*      criteria (spatial and attribute queries).                       */
557 /************************************************************************/
558 
BuildWhere()559 void OGROCITableLayer::BuildWhere()
560 
561 {
562     OGROCIStringBuf oWHERE;
563 
564     CPLFree( pszWHERE );
565     pszWHERE = nullptr;
566 
567     if( m_poFilterGeom != nullptr && bHaveSpatialIndex )
568     {
569         OGREnvelope  sEnvelope;
570 
571         m_poFilterGeom->getEnvelope( &sEnvelope );
572 
573         oWHERE.Append( " WHERE sdo_filter(" );
574         oWHERE.Append( pszGeomName );
575         oWHERE.Append( ", MDSYS.SDO_GEOMETRY(2003," );
576         if( nSRID == -1 )
577             oWHERE.Append( "NULL" );
578         else
579             oWHERE.Appendf( 15, "%d", nSRID );
580         oWHERE.Append( ",NULL," );
581         oWHERE.Append( "MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1)," );
582         oWHERE.Append( "MDSYS.SDO_ORDINATE_ARRAY(" );
583         oWHERE.Appendf( 600,
584                 "%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g",
585                         sEnvelope.MinX, sEnvelope.MinY,
586                         sEnvelope.MaxX, sEnvelope.MinY,
587                         sEnvelope.MaxX, sEnvelope.MaxY,
588                         sEnvelope.MinX, sEnvelope.MaxY,
589                         sEnvelope.MinX, sEnvelope.MinY);
590         oWHERE.Append( ")), 'querytype=window') = 'TRUE' " );
591     }
592 
593     if( bHaveSpatialIndex == HSI_UNKNOWN )
594     {
595         TestForSpatialIndex( oWHERE.GetString() );
596         if( !bHaveSpatialIndex )
597             oWHERE.Clear();
598     }
599 
600     if( pszQuery != nullptr )
601     {
602         if( oWHERE.GetLast() == '\0' )
603             oWHERE.Append( "WHERE " );
604         else
605             oWHERE.Append( "AND " );
606 
607         oWHERE.Append( pszQuery );
608     }
609 
610     pszWHERE = oWHERE.StealString();
611 }
612 
613 /************************************************************************/
614 /*                      BuildFullQueryStatement()                       */
615 /************************************************************************/
616 
BuildFullQueryStatement()617 void OGROCITableLayer::BuildFullQueryStatement()
618 
619 {
620     if( pszQueryStatement != nullptr )
621     {
622         CPLFree( pszQueryStatement );
623         pszQueryStatement = nullptr;
624     }
625 
626     OGROCIStringBuf oCmd;
627     char *pszFields = BuildFields();
628 
629     oCmd.Append( "SELECT " );
630     oCmd.Append( pszFields );
631     oCmd.Append( " FROM " );
632     oCmd.Append( poFeatureDefn->GetName() );
633     oCmd.Append( " " );
634     oCmd.Append( pszWHERE );
635 
636     pszQueryStatement = oCmd.StealString();
637 
638     CPLFree( pszFields );
639 }
640 
641 /************************************************************************/
642 /*                             GetFeature()                             */
643 /************************************************************************/
644 
GetFeature(GIntBig nFeatureId)645 OGRFeature *OGROCITableLayer::GetFeature( GIntBig nFeatureId )
646 
647 {
648 
649 /* -------------------------------------------------------------------- */
650 /*      If we don't have an FID column scan for the desired feature.    */
651 /* -------------------------------------------------------------------- */
652     if( pszFIDName == nullptr )
653         return OGROCILayer::GetFeature( nFeatureId );
654 
655 /* -------------------------------------------------------------------- */
656 /*      Clear any existing query.                                       */
657 /* -------------------------------------------------------------------- */
658     ResetReading();
659 
660 /* -------------------------------------------------------------------- */
661 /*      Build query for this specific feature.                          */
662 /* -------------------------------------------------------------------- */
663     OGROCIStringBuf oCmd;
664     char *pszFields = BuildFields();
665 
666     oCmd.Append( "SELECT " );
667     oCmd.Append( pszFields );
668     oCmd.Append( " FROM " );
669     oCmd.Append( poFeatureDefn->GetName() );
670     oCmd.Append( " " );
671     oCmd.Appendf( static_cast<int>(50+strlen(pszFIDName)),
672                   " WHERE \"%s\" = " CPL_FRMT_GIB " ",
673                   pszFIDName, nFeatureId );
674 
675     CPLFree( pszFields );
676 
677 /* -------------------------------------------------------------------- */
678 /*      Execute the statement.                                          */
679 /* -------------------------------------------------------------------- */
680     if( !ExecuteQuery( oCmd.GetString() ) )
681         return nullptr;
682 
683 /* -------------------------------------------------------------------- */
684 /*      Get the feature.                                                */
685 /* -------------------------------------------------------------------- */
686     OGRFeature *poFeature;
687 
688     poFeature = GetNextRawFeature();
689 
690     if( poFeature != nullptr && poFeature->GetGeometryRef() != nullptr )
691         poFeature->GetGeometryRef()->assignSpatialReference( poSRS );
692 
693 /* -------------------------------------------------------------------- */
694 /*      Cleanup the statement.                                          */
695 /* -------------------------------------------------------------------- */
696     ResetReading();
697 
698 /* -------------------------------------------------------------------- */
699 /*      verify the FID.                                                 */
700 /* -------------------------------------------------------------------- */
701     if( poFeature != nullptr && poFeature->GetFID() != nFeatureId )
702     {
703         CPLError( CE_Failure, CPLE_AppDefined,
704                   "OGROCITableLayer::GetFeature(" CPL_FRMT_GIB ") ... query returned feature " CPL_FRMT_GIB " instead!",
705                   nFeatureId, poFeature->GetFID() );
706         delete poFeature;
707         return nullptr;
708     }
709     else
710         return poFeature;
711 }
712 
713 /************************************************************************/
714 /*                           GetNextFeature()                           */
715 /*                                                                      */
716 /*      We override the next feature method because we know that we     */
717 /*      implement the attribute query within the statement and so we    */
718 /*      don't have to test here.   Eventually the spatial query will    */
719 /*      be fully tested within the statement as well.                   */
720 /************************************************************************/
721 
GetNextFeature()722 OGRFeature *OGROCITableLayer::GetNextFeature()
723 
724 {
725 
726     while( true )
727     {
728         OGRFeature      *poFeature;
729 
730         poFeature = GetNextRawFeature();
731         if( poFeature == nullptr )
732         {
733             CPLDebug( "OCI", "Query complete, got %d hits, and %d discards.",
734                       nHits, nDiscarded );
735             nHits = 0;
736             nDiscarded = 0;
737             return nullptr;
738         }
739 
740         if( m_poFilterGeom == nullptr
741             || FilterGeometry( poFeature->GetGeometryRef() ) )
742         {
743             nHits++;
744             if( poFeature->GetGeometryRef() != nullptr )
745                 poFeature->GetGeometryRef()->assignSpatialReference( poSRS );
746             return poFeature;
747         }
748 
749         if( m_poFilterGeom != nullptr )
750             nDiscarded++;
751 
752         delete poFeature;
753     }
754 }
755 
756 /************************************************************************/
757 /*                            ResetReading()                            */
758 /************************************************************************/
759 
ResetReading()760 void OGROCITableLayer::ResetReading()
761 
762 {
763     nHits = 0;
764     nDiscarded = 0;
765 
766     FlushPendingFeatures();
767 
768     BuildFullQueryStatement();
769 
770     OGROCILayer::ResetReading();
771 }
772 
773 /************************************************************************/
774 /*                            BuildFields()                             */
775 /*                                                                      */
776 /*      Build list of fields to fetch, performing any required          */
777 /*      transformations (such as on geometry).                          */
778 /************************************************************************/
779 
BuildFields()780 char *OGROCITableLayer::BuildFields()
781 
782 {
783     int         i;
784     OGROCIStringBuf oFldList;
785 
786     if( pszGeomName )
787     {
788         oFldList.Append( "\"" );
789         oFldList.Append( pszGeomName );
790         oFldList.Append( "\"" );
791         iGeomColumn = 0;
792     }
793 
794     for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
795     {
796         const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
797 
798         if( oFldList.GetLast() != '\0' )
799             oFldList.Append( "," );
800 
801         oFldList.Append( "\"" );
802         oFldList.Append( pszName );
803         oFldList.Append( "\"" );
804     }
805 
806     if( pszFIDName != nullptr )
807     {
808         iFIDColumn = poFeatureDefn->GetFieldCount();
809         oFldList.Append( ",\"" );
810         oFldList.Append( pszFIDName );
811         oFldList.Append( "\"" );
812     }
813 
814     return oFldList.StealString();
815 }
816 
817 /************************************************************************/
818 /*                         SetAttributeFilter()                         */
819 /************************************************************************/
820 
SetAttributeFilter(const char * pszQueryIn)821 OGRErr OGROCITableLayer::SetAttributeFilter( const char *pszQueryIn )
822 
823 {
824     CPLFree(m_pszAttrQueryString);
825     m_pszAttrQueryString = (pszQueryIn) ? CPLStrdup(pszQueryIn) : nullptr;
826 
827     if( (pszQueryIn == nullptr && this->pszQuery == nullptr)
828         || (pszQueryIn != nullptr && this->pszQuery != nullptr
829             && strcmp(pszQueryIn,this->pszQuery) == 0) )
830         return OGRERR_NONE;
831 
832     CPLFree( this->pszQuery );
833 
834     if( pszQueryIn == nullptr )
835         this->pszQuery = nullptr;
836     else
837         this->pszQuery = CPLStrdup( pszQueryIn );
838 
839     BuildWhere();
840 
841     ResetReading();
842 
843     return OGRERR_NONE;
844 }
845 
846 /************************************************************************/
847 /*                             ISetFeature()                             */
848 /*                                                                      */
849 /*      We implement SetFeature() by deleting the existing row (if      */
850 /*      it exists), and then using CreateFeature() to write it out      */
851 /*      tot he table normally.  CreateFeature() will preserve the       */
852 /*      existing FID if possible.                                       */
853 /************************************************************************/
854 
ISetFeature(OGRFeature * poFeature)855 OGRErr OGROCITableLayer::ISetFeature( OGRFeature *poFeature )
856 
857 {
858 /* -------------------------------------------------------------------- */
859 /*      Do some validation.                                             */
860 /* -------------------------------------------------------------------- */
861     if( pszFIDName == nullptr )
862     {
863         CPLError( CE_Failure, CPLE_AppDefined,
864                   "OGROCITableLayer::ISetFeature(" CPL_FRMT_GIB ") failed because there is "
865                   "no apparent FID column on table %s.",
866                   poFeature->GetFID(),
867                   poFeatureDefn->GetName() );
868 
869         return OGRERR_FAILURE;
870     }
871 
872     if( poFeature->GetFID() == OGRNullFID )
873     {
874         CPLError( CE_Failure, CPLE_AppDefined,
875                   "OGROCITableLayer::ISetFeature(" CPL_FRMT_GIB ") failed because the feature "
876                   "has no FID!", poFeature->GetFID() );
877 
878         return OGRERR_FAILURE;
879     }
880 
881     OGRErr eErr = DeleteFeature(poFeature->GetFID());
882     if( eErr != OGRERR_NONE )
883         return eErr;
884 
885     return CreateFeature( poFeature );
886 }
887 
888 /************************************************************************/
889 /*                           DeleteFeature()                            */
890 /************************************************************************/
891 
DeleteFeature(GIntBig nFID)892 OGRErr OGROCITableLayer::DeleteFeature( GIntBig nFID )
893 
894 {
895 /* -------------------------------------------------------------------- */
896 /*      Do some validation.                                             */
897 /* -------------------------------------------------------------------- */
898     if( pszFIDName == nullptr )
899     {
900         CPLError( CE_Failure, CPLE_AppDefined,
901                   "OGROCITableLayer::DeleteFeature(" CPL_FRMT_GIB ") failed because there is "
902                   "no apparent FID column on table %s.",
903                   nFID,
904                   poFeatureDefn->GetName() );
905 
906         return OGRERR_FAILURE;
907     }
908 
909     if( nFID == OGRNullFID )
910     {
911         CPLError( CE_Failure, CPLE_AppDefined,
912                   "OGROCITableLayer::DeleteFeature(" CPL_FRMT_GIB ") failed for Null FID",
913                   nFID );
914 
915         return OGRERR_FAILURE;
916     }
917 
918 /* -------------------------------------------------------------------- */
919 /*      Prepare the delete command, and execute.  We don't check the    */
920 /*      error result of the execute, since attempting to Set a          */
921 /*      non-existing feature may be OK.                                 */
922 /* -------------------------------------------------------------------- */
923     OGROCIStringBuf     oCmdText;
924     OGROCIStatement     oCmdStatement( poDS->GetSession() );
925 
926     oCmdText.Appendf( static_cast<int>(strlen(poFeatureDefn->GetName())+strlen(pszFIDName)+100),
927                       "DELETE FROM %s WHERE \"%s\" = " CPL_FRMT_GIB,
928                       poFeatureDefn->GetName(),
929                       pszFIDName,
930                       nFID );
931 
932     if( oCmdStatement.Execute( oCmdText.GetString() ) == CE_None )
933         return (oCmdStatement.GetAffectedRows() > 0) ? OGRERR_NONE : OGRERR_NON_EXISTING_FEATURE;
934     else
935         return OGRERR_FAILURE;
936 }
937 
938 /************************************************************************/
939 /*                           ICreateFeature()                            */
940 /************************************************************************/
941 
ICreateFeature(OGRFeature * poFeature)942 OGRErr OGROCITableLayer::ICreateFeature( OGRFeature *poFeature )
943 
944 {
945 /* -------------------------------------------------------------------- */
946 /*      Add extents of this geometry to the existing layer extents.     */
947 /* -------------------------------------------------------------------- */
948     if( poFeature->GetGeometryRef() != nullptr )
949     {
950         OGREnvelope  sThisExtent;
951 
952         poFeature->GetGeometryRef()->getEnvelope( &sThisExtent );
953 
954         if( !sExtent.Contains( sThisExtent ) )
955         {
956             sExtent.Merge( sThisExtent );
957             bExtentUpdated = true;
958         }
959     }
960 
961 /* -------------------------------------------------------------------- */
962 /*      Get the first id value from open options                        */
963 /* -------------------------------------------------------------------- */
964 
965     this->nFirstId = -1;
966 
967     if (CSLFetchNameValue( papszOptions, "FIRST_ID" ) != nullptr)
968     {
969         this->nFirstId = atoi( CSLFetchNameValue( papszOptions, "FIRST_ID" ) );
970     }
971 
972 /* -------------------------------------------------------------------- */
973 /*      Get the multi load count value from open options                */
974 /* -------------------------------------------------------------------- */
975 
976     this->bMultiLoad = CPLFetchBool( papszOptions, "MULTI_LOAD", true );
977 
978     this->nMultiLoadCount = 100;
979 
980     if (CSLFetchNameValue( papszOptions, "MULTI_LOAD_COUNT" ) != nullptr)
981     {
982         this->nMultiLoadCount = atoi( CSLFetchNameValue( papszOptions,
983                                                          "MULTI_LOAD_COUNT" ) );
984         this->bMultiLoad = true; // overwrites MULTI_LOAD=NO
985     }
986 
987 /* -------------------------------------------------------------------- */
988 /*      Do the actual creation.                                         */
989 /* -------------------------------------------------------------------- */
990     if( bMultiLoad )
991         return BoundCreateFeature( poFeature );
992     else
993         return UnboundCreateFeature( poFeature );
994 }
995 
996 /************************************************************************/
997 /*                        UnboundCreateFeature()                        */
998 /************************************************************************/
999 
UnboundCreateFeature(OGRFeature * poFeature)1000 OGRErr OGROCITableLayer::UnboundCreateFeature( OGRFeature *poFeature )
1001 
1002 {
1003     OGROCISession      *poSession = poDS->GetSession();
1004     char                *pszCommand;
1005     int                 bNeedComma = FALSE;
1006     size_t              nCommandBufSize;
1007 
1008 /* -------------------------------------------------------------------- */
1009 /*      Prepare SQL statement buffer.                                   */
1010 /* -------------------------------------------------------------------- */
1011     nCommandBufSize = 2000;
1012     pszCommand = (char *) CPLMalloc(nCommandBufSize);
1013 
1014 /* -------------------------------------------------------------------- */
1015 /*      Form the INSERT command.                                        */
1016 /* -------------------------------------------------------------------- */
1017     snprintf( pszCommand, nCommandBufSize, "INSERT INTO \"%s\"(\"", poFeatureDefn->GetName() );
1018 
1019     if( poFeature->GetGeometryRef() != nullptr )
1020     {
1021         bNeedComma = TRUE;
1022         strcat( pszCommand, pszGeomName );
1023     }
1024 
1025     if( pszFIDName != nullptr )
1026     {
1027         if( bNeedComma )
1028             strcat( pszCommand, "\",\"" );
1029 
1030         strcat( pszCommand, pszFIDName );
1031         bNeedComma = TRUE;
1032     }
1033 
1034     for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1035     {
1036         if( !poFeature->IsFieldSetAndNotNull( i ) )
1037             continue;
1038 
1039         if( !bNeedComma )
1040             bNeedComma = TRUE;
1041         else
1042             strcat( pszCommand, "\",\"" );
1043 
1044         snprintf( pszCommand + strlen(pszCommand),
1045                   nCommandBufSize - strlen(pszCommand), "%s",
1046                  poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1047     }
1048 
1049     strcat( pszCommand, "\") VALUES (" );
1050 
1051     CPLAssert( strlen(pszCommand) < nCommandBufSize );
1052 
1053 /* -------------------------------------------------------------------- */
1054 /*      Set the geometry                                                */
1055 /* -------------------------------------------------------------------- */
1056     bNeedComma = poFeature->GetGeometryRef() != nullptr;
1057     if( poFeature->GetGeometryRef() != nullptr)
1058     {
1059         OGRGeometry *poGeometry = poFeature->GetGeometryRef();
1060         char szSDO_GEOMETRY[512];
1061         char szSRID[128];
1062 
1063         if( nSRID == -1 )
1064             strcpy( szSRID, "NULL" );
1065         else
1066             snprintf( szSRID, sizeof(szSRID), "%d", nSRID );
1067 
1068         if( wkbFlatten(poGeometry->getGeometryType()) == wkbPoint )
1069         {
1070             OGRPoint *poPoint = poGeometry->toPoint();
1071 
1072             if( nDimension == 2 )
1073                 CPLsnprintf( szSDO_GEOMETRY, sizeof(szSDO_GEOMETRY),
1074                          "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,0),NULL,NULL)",
1075                          SDO_GEOMETRY, 2001, szSRID,
1076                          poPoint->getX(), poPoint->getY() );
1077             else
1078                 CPLsnprintf( szSDO_GEOMETRY, sizeof(szSDO_GEOMETRY),
1079                          "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,%.16g),NULL,NULL)",
1080                          SDO_GEOMETRY, 3001, szSRID,
1081                          poPoint->getX(), poPoint->getY(), poPoint->getZ() );
1082         }
1083         else
1084         {
1085             int  nGType;
1086 
1087             if( TranslateToSDOGeometry( poFeature->GetGeometryRef(), &nGType )
1088                 == OGRERR_NONE )
1089                 CPLsnprintf( szSDO_GEOMETRY, sizeof(szSDO_GEOMETRY),
1090                          "%s(%d,%s,NULL,:elem_info,:ordinates)",
1091                          SDO_GEOMETRY, nGType, szSRID );
1092             else
1093                 CPLsnprintf( szSDO_GEOMETRY, sizeof(szSDO_GEOMETRY),"NULL" );
1094         }
1095 
1096         if( strlen(pszCommand) + strlen(szSDO_GEOMETRY)
1097             > nCommandBufSize - 50 )
1098         {
1099             nCommandBufSize =
1100                strlen(pszCommand) + strlen(szSDO_GEOMETRY) + 10000;
1101             pszCommand = (char *) CPLRealloc(pszCommand, nCommandBufSize );
1102         }
1103 
1104         strcat( pszCommand, szSDO_GEOMETRY );
1105     }
1106 
1107 /* -------------------------------------------------------------------- */
1108 /*      Set the FID.                                                    */
1109 /* -------------------------------------------------------------------- */
1110     size_t nOffset = strlen(pszCommand);
1111 
1112     if( pszFIDName != nullptr )
1113     {
1114         GIntBig  nFID;
1115 
1116         if( bNeedComma )
1117             strcat( pszCommand+nOffset, ", " );
1118         bNeedComma = TRUE;
1119 
1120         nOffset += strlen(pszCommand+nOffset);
1121 
1122         nFID = poFeature->GetFID();
1123         if( nFID == OGRNullFID )
1124         {
1125             if( iNextFIDToWrite < 0 )
1126             {
1127                 iNextFIDToWrite = GetMaxFID() + 1;
1128             }
1129             nFID = iNextFIDToWrite++;
1130             poFeature->SetFID( nFID );
1131         }
1132         snprintf( pszCommand+nOffset, nCommandBufSize - nOffset, CPL_FRMT_GIB, nFID );
1133     }
1134 
1135 /* -------------------------------------------------------------------- */
1136 /*      Set the other fields.                                           */
1137 /* -------------------------------------------------------------------- */
1138     for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1139     {
1140         if( !poFeature->IsFieldSetAndNotNull( i ) )
1141             continue;
1142 
1143         OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i);
1144         const char *pszStrValue = poFeature->GetFieldAsString(i);
1145 
1146         if( bNeedComma )
1147             strcat( pszCommand+nOffset, ", " );
1148         else
1149             bNeedComma = TRUE;
1150 
1151         if( strlen(pszStrValue) + strlen(pszCommand+nOffset) + nOffset
1152             > nCommandBufSize-50 )
1153         {
1154             nCommandBufSize = strlen(pszCommand) + strlen(pszStrValue) + 10000;
1155             pszCommand = (char *) CPLRealloc(pszCommand, nCommandBufSize );
1156         }
1157 
1158         if( poFldDefn->GetType() == OFTInteger
1159             || poFldDefn->GetType() == OFTInteger64
1160             || poFldDefn->GetType() == OFTReal )
1161         {
1162             if( poFldDefn->GetWidth() > 0 && bPreservePrecision
1163                 && (int) strlen(pszStrValue) > poFldDefn->GetWidth() )
1164             {
1165                 strcat( pszCommand+nOffset, "NULL" );
1166                 ReportTruncation( poFldDefn );
1167             }
1168             else
1169                 strcat( pszCommand+nOffset, pszStrValue );
1170         }
1171         else
1172         {
1173             int         iChar;
1174 
1175             /* We need to quote and escape string fields. */
1176             strcat( pszCommand+nOffset, "'" );
1177 
1178             nOffset += strlen(pszCommand+nOffset);
1179 
1180             for( iChar = 0; pszStrValue[iChar] != '\0'; iChar++ )
1181             {
1182                 if( poFldDefn->GetWidth() != 0 && bPreservePrecision
1183                     && iChar >= poFldDefn->GetWidth() )
1184                 {
1185                     ReportTruncation( poFldDefn );
1186                     break;
1187                 }
1188 
1189                 if( pszStrValue[iChar] == '\'' )
1190                 {
1191                     pszCommand[nOffset++] = '\'';
1192                     pszCommand[nOffset++] = pszStrValue[iChar];
1193                 }
1194                 else
1195                     pszCommand[nOffset++] = pszStrValue[iChar];
1196             }
1197             pszCommand[nOffset] = '\0';
1198 
1199             strcat( pszCommand+nOffset, "'" );
1200         }
1201         nOffset += strlen(pszCommand+nOffset);
1202     }
1203 
1204     strcat( pszCommand+nOffset, ")" );
1205 
1206 /* -------------------------------------------------------------------- */
1207 /*      Prepare statement.                                              */
1208 /* -------------------------------------------------------------------- */
1209     OGROCIStatement oInsert( poSession );
1210     int  bHaveOrdinates = strstr(pszCommand,":ordinates") != nullptr;
1211     int  bHaveElemInfo = strstr(pszCommand,":elem_info") != nullptr;
1212 
1213     if( oInsert.Prepare( pszCommand ) != CE_None )
1214     {
1215         CPLFree( pszCommand );
1216         return OGRERR_FAILURE;
1217     }
1218 
1219     CPLFree( pszCommand );
1220 
1221 /* -------------------------------------------------------------------- */
1222 /*      Bind and translate the elem_info if we have some.               */
1223 /* -------------------------------------------------------------------- */
1224     if( bHaveElemInfo )
1225     {
1226         OCIBind *hBindOrd = nullptr;
1227         int i;
1228         OCINumber oci_number;
1229 
1230         // Create or clear VARRAY
1231         if( hElemInfoVARRAY == nullptr )
1232         {
1233             if( poSession->Failed(
1234                 OCIObjectNew( poSession->hEnv, poSession->hError,
1235                               poSession->hSvcCtx, OCI_TYPECODE_VARRAY,
1236                               poSession->hElemInfoTDO, (dvoid *)nullptr,
1237                               OCI_DURATION_SESSION,
1238                               FALSE, (dvoid **)&hElemInfoVARRAY),
1239                 "OCIObjectNew(hElemInfoVARRAY)") )
1240                 return OGRERR_FAILURE;
1241         }
1242         else
1243         {
1244             sb4  nOldCount;
1245 
1246             OCICollSize( poSession->hEnv, poSession->hError,
1247                          hElemInfoVARRAY, &nOldCount );
1248             OCICollTrim( poSession->hEnv, poSession->hError,
1249                          nOldCount, hElemInfoVARRAY );
1250         }
1251 
1252         // Prepare the VARRAY of ordinate values.
1253         for (i = 0; i < nElemInfoCount; i++)
1254         {
1255             if( poSession->Failed(
1256                 OCINumberFromInt( poSession->hError,
1257                                   (dvoid *) (panElemInfo + i),
1258                                   (uword)sizeof(int),
1259                                   OCI_NUMBER_SIGNED,
1260                                   &oci_number),
1261                 "OCINumberFromInt") )
1262                 return OGRERR_FAILURE;
1263 
1264             if( poSession->Failed(
1265                 OCICollAppend( poSession->hEnv, poSession->hError,
1266                                (dvoid *) &oci_number,
1267                                (dvoid *)nullptr, hElemInfoVARRAY),
1268                 "OCICollAppend") )
1269                 return OGRERR_FAILURE;
1270         }
1271 
1272         // Do the binding.
1273         if( poSession->Failed(
1274             OCIBindByName( oInsert.GetStatement(), &hBindOrd,
1275                            poSession->hError,
1276                            (text *) ":elem_info", (sb4) -1, (dvoid *) nullptr,
1277                            (sb4) 0, SQLT_NTY, (dvoid *)nullptr, (ub2 *)nullptr,
1278                            (ub2 *)nullptr, (ub4)0, (ub4 *)nullptr,
1279                            (ub4)OCI_DEFAULT),
1280             "OCIBindByName(:elem_info)") )
1281             return OGRERR_FAILURE;
1282 
1283         if( poSession->Failed(
1284             OCIBindObject( hBindOrd, poSession->hError,
1285                            poSession->hElemInfoTDO,
1286                            (dvoid **)&hElemInfoVARRAY, (ub4 *)nullptr,
1287                            (dvoid **)nullptr, (ub4 *)nullptr),
1288             "OCIBindObject(:elem_info)" ) )
1289             return OGRERR_FAILURE;
1290     }
1291 
1292 /* -------------------------------------------------------------------- */
1293 /*      Bind and translate the ordinates if we have some.               */
1294 /* -------------------------------------------------------------------- */
1295     if( bHaveOrdinates )
1296     {
1297         OCIBind *hBindOrd = nullptr;
1298         int i;
1299         OCINumber oci_number;
1300 
1301         // Create or clear VARRAY
1302         if( hOrdVARRAY == nullptr )
1303         {
1304             if( poSession->Failed(
1305                 OCIObjectNew( poSession->hEnv, poSession->hError,
1306                               poSession->hSvcCtx, OCI_TYPECODE_VARRAY,
1307                               poSession->hOrdinatesTDO, (dvoid *)nullptr,
1308                               OCI_DURATION_SESSION,
1309                               FALSE, (dvoid **)&hOrdVARRAY),
1310                 "OCIObjectNew(hOrdVARRAY)") )
1311                 return OGRERR_FAILURE;
1312         }
1313         else
1314         {
1315             sb4  nOldCount;
1316 
1317             OCICollSize( poSession->hEnv, poSession->hError,
1318                          hOrdVARRAY, &nOldCount );
1319             OCICollTrim( poSession->hEnv, poSession->hError,
1320                          nOldCount, hOrdVARRAY );
1321         }
1322 
1323         // Prepare the VARRAY of ordinate values.
1324         for (i = 0; i < nOrdinalCount; i++)
1325         {
1326             if( poSession->Failed(
1327                 OCINumberFromReal( poSession->hError,
1328                                    (dvoid *) (padfOrdinals + i),
1329                                    (uword)sizeof(double),
1330                                    &oci_number),
1331                 "OCINumberFromReal") )
1332                 return OGRERR_FAILURE;
1333 
1334             if( poSession->Failed(
1335                 OCICollAppend( poSession->hEnv, poSession->hError,
1336                                (dvoid *) &oci_number,
1337                                (dvoid *)nullptr, hOrdVARRAY),
1338                 "OCICollAppend") )
1339                 return OGRERR_FAILURE;
1340         }
1341 
1342         // Do the binding.
1343         if( poSession->Failed(
1344             OCIBindByName( oInsert.GetStatement(), &hBindOrd,
1345                            poSession->hError,
1346                            (text *) ":ordinates", (sb4) -1, (dvoid *) nullptr,
1347                            (sb4) 0, SQLT_NTY, (dvoid *)nullptr, (ub2 *)nullptr,
1348                            (ub2 *)nullptr, (ub4)0, (ub4 *)nullptr,
1349                            (ub4)OCI_DEFAULT),
1350             "OCIBindByName(:ordinates)") )
1351             return OGRERR_FAILURE;
1352 
1353         if( poSession->Failed(
1354             OCIBindObject( hBindOrd, poSession->hError,
1355                            poSession->hOrdinatesTDO,
1356                            (dvoid **)&hOrdVARRAY, (ub4 *)nullptr,
1357                            (dvoid **)nullptr, (ub4 *)nullptr),
1358             "OCIBindObject(:ordinates)" ) )
1359             return OGRERR_FAILURE;
1360     }
1361 
1362 /* -------------------------------------------------------------------- */
1363 /*      Execute the insert.                                             */
1364 /* -------------------------------------------------------------------- */
1365     if( oInsert.Execute( nullptr ) != CE_None )
1366         return OGRERR_FAILURE;
1367     else
1368         return OGRERR_NONE;
1369 }
1370 
1371 /************************************************************************/
1372 /*                           GetExtent()                                */
1373 /************************************************************************/
1374 
GetExtent(OGREnvelope * psExtent,int bForce)1375 OGRErr OGROCITableLayer::GetExtent(OGREnvelope *psExtent, int bForce)
1376 
1377 {
1378     CPLAssert( nullptr != psExtent );
1379 
1380     OGRErr err = OGRERR_FAILURE;
1381 
1382     if( EQUAL(GetGeometryColumn(),"") )
1383     {
1384         return OGRERR_NONE;
1385     }
1386 
1387 /* -------------------------------------------------------------------- */
1388 /*      Build query command.                                        */
1389 /* -------------------------------------------------------------------- */
1390     CPLAssert( nullptr != pszGeomName );
1391 
1392     OGROCIStringBuf oCommand;
1393     oCommand.Appendf( 1000, "SELECT "
1394                       "MIN(SDO_GEOM.SDO_MIN_MBR_ORDINATE(t.%s,m.DIMINFO,1)) AS MINX,"
1395                       "MIN(SDO_GEOM.SDO_MIN_MBR_ORDINATE(t.%s,m.DIMINFO,2)) AS MINY,"
1396                       "MAX(SDO_GEOM.SDO_MAX_MBR_ORDINATE(t.%s,m.DIMINFO,1)) AS MAXX,"
1397                       "MAX(SDO_GEOM.SDO_MAX_MBR_ORDINATE(t.%s,m.DIMINFO,2)) AS MAXY "
1398                       "FROM ALL_SDO_GEOM_METADATA m, ",
1399                       pszGeomName, pszGeomName, pszGeomName, pszGeomName );
1400 
1401     if( osOwner != "" )
1402     {
1403         oCommand.Appendf( 500, " %s.%s t ",
1404                           osOwner.c_str(), osTableName.c_str() );
1405     }
1406     else
1407     {
1408         oCommand.Appendf( 500, " %s t ",
1409                           osTableName.c_str() );
1410     }
1411 
1412     oCommand.Appendf( 500, "WHERE m.TABLE_NAME = UPPER('%s') AND m.COLUMN_NAME = UPPER('%s')",
1413                       osTableName.c_str(), pszGeomName );
1414 
1415     if( osOwner != "" )
1416     {
1417         oCommand.Appendf( 500, " AND OWNER = UPPER('%s')", osOwner.c_str() );
1418     }
1419 
1420 /* -------------------------------------------------------------------- */
1421 /*      Execute query command.                                          */
1422 /* -------------------------------------------------------------------- */
1423     OGROCISession *poSession = poDS->GetSession();
1424     CPLAssert( nullptr != poSession );
1425 
1426     OGROCIStatement oGetExtent( poSession );
1427 
1428     if( oGetExtent.Execute( oCommand.GetString() ) == CE_None )
1429     {
1430         char **papszRow = oGetExtent.SimpleFetchRow();
1431 
1432         if( papszRow != nullptr
1433             && papszRow[0] != nullptr && papszRow[1] != nullptr
1434             && papszRow[2] != nullptr && papszRow[3] != nullptr )
1435         {
1436             psExtent->MinX = CPLAtof(papszRow[0]);
1437             psExtent->MinY = CPLAtof(papszRow[1]);
1438             psExtent->MaxX = CPLAtof(papszRow[2]);
1439             psExtent->MaxY = CPLAtof(papszRow[3]);
1440 
1441             err = OGRERR_NONE;
1442         }
1443     }
1444 
1445 /* -------------------------------------------------------------------- */
1446 /*      Query spatial extent of layer using default,                    */
1447 /*      but not optimized implementation.                               */
1448 /* -------------------------------------------------------------------- */
1449     if( err != OGRERR_NONE )
1450     {
1451         err = OGRLayer::GetExtent( psExtent, bForce );
1452         CPLDebug( "OCI",
1453                   "Failing to query extent of %s using default GetExtent",
1454                   osTableName.c_str() );
1455     }
1456 
1457     return err;
1458 }
1459 
1460 /************************************************************************/
1461 /*                           TestCapability()                           */
1462 /************************************************************************/
1463 
TestCapability(const char * pszCap)1464 int OGROCITableLayer::TestCapability( const char * pszCap )
1465 
1466 {
1467     if( EQUAL(pszCap,OLCSequentialWrite)
1468              || EQUAL(pszCap,OLCRandomWrite) )
1469         return bUpdateAccess;
1470 
1471     else if( EQUAL(pszCap,OLCCreateField) )
1472         return bUpdateAccess;
1473 
1474     else
1475         return OGROCILayer::TestCapability( pszCap );
1476 }
1477 
1478 /************************************************************************/
1479 /*                          GetFeatureCount()                           */
1480 /*                                                                      */
1481 /*      If a spatial filter is in effect, we turn control over to       */
1482 /*      the generic counter.  Otherwise we return the total count.      */
1483 /*      Eventually we should consider implementing a more efficient     */
1484 /*      way of counting features matching a spatial query.              */
1485 /************************************************************************/
1486 
GetFeatureCount(int bForce)1487 GIntBig OGROCITableLayer::GetFeatureCount( int bForce )
1488 
1489 {
1490 /* -------------------------------------------------------------------- */
1491 /*      Use a more brute force mechanism if we have a spatial query     */
1492 /*      in play.                                                        */
1493 /* -------------------------------------------------------------------- */
1494     if( m_poFilterGeom != nullptr )
1495         return OGROCILayer::GetFeatureCount( bForce );
1496 
1497 /* -------------------------------------------------------------------- */
1498 /*      In theory it might be wise to cache this result, but it         */
1499 /*      won't be trivial to work out the lifetime of the value.         */
1500 /*      After all someone else could be adding records from another     */
1501 /*      application when working against a database.                    */
1502 /* -------------------------------------------------------------------- */
1503     OGROCISession      *poSession = poDS->GetSession();
1504     OGROCIStatement    oGetCount( poSession );
1505     char               szCommand[1024];
1506     char               **papszResult;
1507 
1508     snprintf( szCommand, sizeof(szCommand), "SELECT COUNT(*) FROM %s %s",
1509              poFeatureDefn->GetName(), pszWHERE );
1510 
1511     oGetCount.Execute( szCommand );
1512 
1513     papszResult = oGetCount.SimpleFetchRow();
1514 
1515     if( CSLCount(papszResult) < 1 )
1516     {
1517         CPLDebug( "OCI", "Fast get count failed, doing hard way." );
1518         return OGROCILayer::GetFeatureCount( bForce );
1519     }
1520 
1521     return CPLAtoGIntBig(papszResult[0]);
1522 }
1523 
1524 /************************************************************************/
1525 /*                         UpdateLayerExtents()                         */
1526 /************************************************************************/
1527 
UpdateLayerExtents()1528 void OGROCITableLayer::UpdateLayerExtents()
1529 
1530 {
1531     if( !bExtentUpdated )
1532         return;
1533 
1534     bExtentUpdated = false;
1535 
1536 /* -------------------------------------------------------------------- */
1537 /*      Do we have existing layer extents we need to merge in to the    */
1538 /*      ones we collected as we created features?                       */
1539 /* -------------------------------------------------------------------- */
1540     bool bHaveOldExtent = false;
1541 
1542     if( !bNewLayer && pszGeomName )
1543     {
1544         OGROCIStringBuf oCommand;
1545 
1546         oCommand.Appendf(1000,
1547                           "select min(case when r=1 then sdo_lb else null end) minx, min(case when r=2 then sdo_lb else null end) miny, "
1548                           "min(case when r=1 then sdo_ub else null end) maxx, min(case when r=2 then sdo_ub else null end) maxy"
1549                           " from (SELECT d.sdo_dimname, d.sdo_lb, sdo_ub, sdo_tolerance, rownum r"
1550                           " FROM ALL_SDO_GEOM_METADATA m, table(m.diminfo) d"
1551                           " where m.table_name = UPPER('%s') and m.COLUMN_NAME = UPPER('%s')",
1552                           osTableName.c_str(), pszGeomName );
1553 
1554         if( osOwner != "" )
1555         {
1556             oCommand.Appendf(500, " AND OWNER = UPPER('%s')", osOwner.c_str() );
1557         }
1558 
1559         oCommand.Append(" ) ");
1560 
1561         OGROCISession *poSession = poDS->GetSession();
1562         CPLAssert( nullptr != poSession );
1563 
1564         OGROCIStatement oGetExtent( poSession );
1565 
1566         if( oGetExtent.Execute( oCommand.GetString() ) == CE_None )
1567         {
1568             char **papszRow = oGetExtent.SimpleFetchRow();
1569 
1570             if( papszRow != nullptr
1571                 && papszRow[0] != nullptr && papszRow[1] != nullptr
1572                 && papszRow[2] != nullptr && papszRow[3] != nullptr )
1573             {
1574                 OGREnvelope sOldExtent;
1575 
1576                 bHaveOldExtent = true;
1577 
1578                 sOldExtent.MinX = CPLAtof(papszRow[0]);
1579                 sOldExtent.MinY = CPLAtof(papszRow[1]);
1580                 sOldExtent.MaxX = CPLAtof(papszRow[2]);
1581                 sOldExtent.MaxY = CPLAtof(papszRow[3]);
1582 
1583                 if( sOldExtent.Contains( sExtent ) )
1584                 {
1585                     // nothing to do!
1586                     sExtent = sOldExtent;
1587                     bExtentUpdated = false;
1588                     return;
1589                 }
1590                 else
1591                 {
1592                     sExtent.Merge( sOldExtent );
1593                 }
1594             }
1595         }
1596     }
1597 
1598 /* -------------------------------------------------------------------- */
1599 /*      Establish the extents and resolution to use.                    */
1600 /* -------------------------------------------------------------------- */
1601     double           dfResSize;
1602     double           dfXMin, dfXMax, dfXRes;
1603     double           dfYMin, dfYMax, dfYRes;
1604     double           dfZMin, dfZMax, dfZRes;
1605 
1606     if( sExtent.MaxX - sExtent.MinX > 400 )
1607         dfResSize = 0.001;
1608     else
1609         dfResSize = 0.0000001;
1610 
1611     dfXMin = sExtent.MinX - dfResSize * 3;
1612     dfXMax = sExtent.MaxX + dfResSize * 3;
1613     dfXRes = dfResSize;
1614     ParseDIMINFO( "DIMINFO_X", &dfXMin, &dfXMax, &dfXRes );
1615 
1616     dfYMin = sExtent.MinY - dfResSize * 3;
1617     dfYMax = sExtent.MaxY + dfResSize * 3;
1618     dfYRes = dfResSize;
1619     ParseDIMINFO( "DIMINFO_Y", &dfYMin, &dfYMax, &dfYRes );
1620 
1621     dfZMin = -100000.0;
1622     dfZMax = 100000.0;
1623     dfZRes = 0.002;
1624     ParseDIMINFO( "DIMINFO_Z", &dfZMin, &dfZMax, &dfZRes );
1625 
1626 /* -------------------------------------------------------------------- */
1627 /*      If we already have an extent in the table, we will need to      */
1628 /*      update it in place.                                             */
1629 /* -------------------------------------------------------------------- */
1630     OGROCIStringBuf  sDimUpdate;
1631 
1632     if( bHaveOldExtent )
1633     {
1634         sDimUpdate.Append( "UPDATE USER_SDO_GEOM_METADATA " );
1635         sDimUpdate.Append( "SET DIMINFO =" );
1636         sDimUpdate.Append( "MDSYS.SDO_DIM_ARRAY(" );
1637         sDimUpdate.Appendf(200,
1638                            "MDSYS.SDO_DIM_ELEMENT('X',%.16g,%.16g,%.12g)",
1639                            dfXMin, dfXMax, dfXRes );
1640         sDimUpdate.Appendf(200,
1641                            ",MDSYS.SDO_DIM_ELEMENT('Y',%.16g,%.16g,%.12g)",
1642                            dfYMin, dfYMax, dfYRes );
1643 
1644         if( nDimension == 3 )
1645         {
1646             sDimUpdate.Appendf(200,
1647                                ",MDSYS.SDO_DIM_ELEMENT('Z',%.16g,%.16g,%.12g)",
1648                                dfZMin, dfZMax, dfZRes );
1649         }
1650 
1651         sDimUpdate.Appendf(static_cast<int>(strlen(poFeatureDefn->GetName()) + 100),
1652                            ") WHERE TABLE_NAME = '%s'", poFeatureDefn->GetName());
1653     }
1654     else
1655     {
1656 /* -------------------------------------------------------------------- */
1657 /*      Prepare dimension update statement.                             */
1658 /* -------------------------------------------------------------------- */
1659         sDimUpdate.Append( "INSERT INTO USER_SDO_GEOM_METADATA VALUES " );
1660         sDimUpdate.Appendf( static_cast<int>(strlen(poFeatureDefn->GetName()) + 100),
1661                             "('%s', '%s', ",
1662                             poFeatureDefn->GetName(),
1663                             pszGeomName );
1664 
1665         sDimUpdate.Append( "MDSYS.SDO_DIM_ARRAY(" );
1666         sDimUpdate.Appendf(200,
1667                            "MDSYS.SDO_DIM_ELEMENT('X',%.16g,%.16g,%.12g)",
1668                            dfXMin, dfXMax, dfXRes );
1669         sDimUpdate.Appendf(200,
1670                            ",MDSYS.SDO_DIM_ELEMENT('Y',%.16g,%.16g,%.12g)",
1671                            dfYMin, dfYMax, dfYRes );
1672 
1673         if( nDimension == 3 )
1674         {
1675             sDimUpdate.Appendf(200,
1676                                ",MDSYS.SDO_DIM_ELEMENT('Z',%.16g,%.16g,%.12g)",
1677                                dfZMin, dfZMax, dfZRes );
1678         }
1679 
1680         if( nSRID == -1 )
1681             sDimUpdate.Append( "), NULL)" );
1682         else
1683             sDimUpdate.Appendf( 100, "), %d)", nSRID );
1684     }
1685 
1686 /* -------------------------------------------------------------------- */
1687 /*      Run the update/insert command.                                  */
1688 /* -------------------------------------------------------------------- */
1689     OGROCIStatement oExecStatement( poDS->GetSession() );
1690 
1691     oExecStatement.Execute( sDimUpdate.GetString() );
1692 }
1693 
1694 /************************************************************************/
1695 /*                   AllocAndBindForWrite()                             */
1696 /************************************************************************/
1697 
AllocAndBindForWrite()1698 int OGROCITableLayer::AllocAndBindForWrite()
1699 
1700 {
1701     OGROCISession      *poSession = poDS->GetSession();
1702     int i;
1703 
1704     CPLAssert( nWriteCacheMax == 0 );
1705 
1706 /* -------------------------------------------------------------------- */
1707 /*      Decide on the number of rows we want to be able to cache at     */
1708 /*      a time.                                                         */
1709 /* -------------------------------------------------------------------- */
1710     nWriteCacheMax = nMultiLoadCount;
1711 
1712 /* -------------------------------------------------------------------- */
1713 /*      Collect the INSERT statement.                                   */
1714 /* -------------------------------------------------------------------- */
1715     OGROCIStringBuf oCmdBuf;
1716 
1717     oCmdBuf.Append( "INSERT /*+ APPEND */ INTO \"" );
1718     oCmdBuf.Append( poFeatureDefn->GetName() );
1719     oCmdBuf.Append( "\"(\"" );
1720     oCmdBuf.Append( pszFIDName );
1721 
1722     if (GetGeomType() != wkbNone)
1723     {
1724        oCmdBuf.Append( "\",\"" );
1725        oCmdBuf.Append( pszGeomName );
1726     }
1727 
1728     for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1729     {
1730         oCmdBuf.Append( "\",\"" );
1731         oCmdBuf.Append( poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
1732     }
1733 
1734     oCmdBuf.Append( "\") VALUES ( :fid " );
1735 
1736     if (GetGeomType() != wkbNone)
1737         oCmdBuf.Append( ", :geometry" );
1738 
1739     for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1740     {
1741         oCmdBuf.Append( ", " );
1742         oCmdBuf.Appendf( 20, " :field_%d", i );
1743     }
1744 
1745     oCmdBuf.Append( ") " );
1746 
1747 /* -------------------------------------------------------------------- */
1748 /*      Bind and Prepare it.                                            */
1749 /* -------------------------------------------------------------------- */
1750     poBoundStatement = new OGROCIStatement( poSession );
1751     poBoundStatement->Prepare( oCmdBuf.GetString() );
1752 
1753 /* -------------------------------------------------------------------- */
1754 /*      Setup geometry indicator information.                           */
1755 /* -------------------------------------------------------------------- */
1756     if (GetGeomType() != wkbNone)
1757     {
1758         pasWriteGeomInd = (SDO_GEOMETRY_ind *)
1759             CPLCalloc(sizeof(SDO_GEOMETRY_ind),nWriteCacheMax);
1760 
1761         papsWriteGeomIndMap = (SDO_GEOMETRY_ind **)
1762             CPLCalloc(sizeof(SDO_GEOMETRY_ind *),nWriteCacheMax);
1763 
1764         for( i = 0; i < nWriteCacheMax; i++ )
1765             papsWriteGeomIndMap[i] = pasWriteGeomInd + i;
1766 
1767 /* -------------------------------------------------------------------- */
1768 /*      Setup all the required geometry objects, and the                */
1769 /*      corresponding indicator map.                                    */
1770 /* -------------------------------------------------------------------- */
1771         pasWriteGeoms = (SDO_GEOMETRY_TYPE *)
1772             CPLCalloc( sizeof(SDO_GEOMETRY_TYPE), nWriteCacheMax);
1773         papsWriteGeomMap = (SDO_GEOMETRY_TYPE **)
1774             CPLCalloc( sizeof(SDO_GEOMETRY_TYPE *), nWriteCacheMax );
1775 
1776         for( i = 0; i < nWriteCacheMax; i++ )
1777             papsWriteGeomMap[i] = pasWriteGeoms + i;
1778 
1779 /* -------------------------------------------------------------------- */
1780 /*      Allocate VARRAYs for the elem_info and ordinates.               */
1781 /* -------------------------------------------------------------------- */
1782         for( i = 0; i < nWriteCacheMax; i++ )
1783         {
1784             if( poSession->Failed(
1785                 OCIObjectNew( poSession->hEnv, poSession->hError,
1786                               poSession->hSvcCtx, OCI_TYPECODE_VARRAY,
1787                               poSession->hElemInfoTDO, (dvoid *)nullptr,
1788                               OCI_DURATION_SESSION,
1789                               FALSE,
1790                               (dvoid **) &(pasWriteGeoms[i].sdo_elem_info)),
1791                 "OCIObjectNew(elem_info)") )
1792                 return FALSE;
1793 
1794             if( poSession->Failed(
1795                 OCIObjectNew( poSession->hEnv, poSession->hError,
1796                               poSession->hSvcCtx, OCI_TYPECODE_VARRAY,
1797                               poSession->hOrdinatesTDO, (dvoid *)nullptr,
1798                               OCI_DURATION_SESSION,
1799                               FALSE,
1800                               (dvoid **) &(pasWriteGeoms[i].sdo_ordinates)),
1801                 "OCIObjectNew(ordinates)") )
1802                 return FALSE;
1803         }
1804 
1805 /* -------------------------------------------------------------------- */
1806 /*      Bind the geometry column.                                       */
1807 /* -------------------------------------------------------------------- */
1808         if( poBoundStatement->BindObject(
1809             ":geometry", papsWriteGeomMap, poSession->hGeometryTDO,
1810             (void**) papsWriteGeomIndMap) != CE_None )
1811             return FALSE;
1812     }
1813 
1814 /* -------------------------------------------------------------------- */
1815 /*      Bind the FID column.                                            */
1816 /* -------------------------------------------------------------------- */
1817     panWriteFIDs = (int *) CPLMalloc(sizeof(int) * nWriteCacheMax );
1818 
1819     if( poBoundStatement->BindScalar( ":fid", panWriteFIDs, sizeof(int),
1820                                       SQLT_INT ) != CE_None )
1821         return FALSE;
1822 
1823 /* -------------------------------------------------------------------- */
1824 /*      Allocate each of the column data bind arrays.                   */
1825 /* -------------------------------------------------------------------- */
1826 
1827     papWriteFields = (void **)
1828         CPLMalloc(sizeof(void*) * poFeatureDefn->GetFieldCount() );
1829     papaeWriteFieldInd = (OCIInd **)
1830         CPLCalloc(sizeof(OCIInd*),poFeatureDefn->GetFieldCount() );
1831 
1832     for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1833     {
1834         OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i);
1835         char szFieldPlaceholderName[80];
1836 
1837         snprintf( szFieldPlaceholderName, sizeof(szFieldPlaceholderName), ":field_%d", i );
1838 
1839         papaeWriteFieldInd[i] = (OCIInd *)
1840             CPLCalloc(sizeof(OCIInd), nWriteCacheMax );
1841 
1842         if( poFldDefn->GetType() == OFTInteger )
1843         {
1844             papWriteFields[i] =
1845                 (void *) CPLCalloc( sizeof(int), nWriteCacheMax );
1846 
1847             if( poBoundStatement->BindScalar(
1848                     szFieldPlaceholderName, papWriteFields[i],
1849                     sizeof(int), SQLT_INT, papaeWriteFieldInd[i] ) != CE_None )
1850                 return FALSE;
1851         }
1852         else if( poFldDefn->GetType() == OFTInteger64 )
1853         {
1854             papWriteFields[i] =
1855                 (void *) CPLCalloc( sizeof(GIntBig), nWriteCacheMax );
1856 
1857             if( poBoundStatement->BindScalar(
1858                     szFieldPlaceholderName, papWriteFields[i],
1859                     sizeof(GIntBig), SQLT_INT, papaeWriteFieldInd[i] ) != CE_None )
1860                 return FALSE;
1861         }
1862         else if( poFldDefn->GetType() == OFTReal )
1863         {
1864             papWriteFields[i] = (void *) CPLCalloc( sizeof(double),
1865                                                     nWriteCacheMax );
1866 
1867             if( poBoundStatement->BindScalar(
1868                     szFieldPlaceholderName, papWriteFields[i],
1869                     sizeof(double), SQLT_FLT, papaeWriteFieldInd[i] ) != CE_None )
1870                 return FALSE;
1871         }
1872         else
1873         {
1874             int nEachBufSize = nDefaultStringSize + 1;
1875 
1876             if( poFldDefn->GetType() == OFTString
1877                 && poFldDefn->GetWidth() != 0 )
1878                 nEachBufSize = poFldDefn->GetWidth() + 1;
1879 
1880             papWriteFields[i] =
1881                 (void *) CPLCalloc( nEachBufSize, nWriteCacheMax );
1882 
1883             if( poBoundStatement->BindScalar(
1884                     szFieldPlaceholderName, papWriteFields[i],
1885                     nEachBufSize, SQLT_STR, papaeWriteFieldInd[i]) != CE_None )
1886                 return FALSE;
1887         }
1888     }
1889 
1890     return TRUE;
1891 }
1892 
1893 /************************************************************************/
1894 /*                         BoundCreateFeature()                         */
1895 /************************************************************************/
1896 
BoundCreateFeature(OGRFeature * poFeature)1897 OGRErr OGROCITableLayer::BoundCreateFeature( OGRFeature *poFeature )
1898 
1899 {
1900     OGROCISession      *poSession = poDS->GetSession();
1901     int                iCache, i;
1902     OGRErr             eErr;
1903     OCINumber          oci_number;
1904 
1905     /* If an unset field has a default value, the current implementation */
1906     /* of BoundCreateFeature() doesn't work. */
1907     for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
1908     {
1909         if( !poFeature->IsFieldSetAndNotNull( i ) &&
1910             poFeature->GetFieldDefnRef(i)->GetDefault() != nullptr )
1911         {
1912             FlushPendingFeatures();
1913             return UnboundCreateFeature(poFeature);
1914         }
1915     }
1916 
1917     if( !poFeature->Validate( OGR_F_VAL_NULL | OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT, TRUE ) )
1918         return OGRERR_FAILURE;
1919 
1920     iCache = nWriteCacheUsed;
1921 
1922 /* -------------------------------------------------------------------- */
1923 /*  Initiate the Insert                                                 */
1924 /* -------------------------------------------------------------------- */
1925     if( nWriteCacheMax == 0 )
1926     {
1927         if( !AllocAndBindForWrite() )
1928             return OGRERR_FAILURE;
1929     }
1930 
1931 /* -------------------------------------------------------------------- */
1932 /*      Set the geometry                                                */
1933 /* -------------------------------------------------------------------- */
1934     if( poFeature->GetGeometryRef() != nullptr )
1935     {
1936         SDO_GEOMETRY_TYPE *psGeom = pasWriteGeoms + iCache;
1937         SDO_GEOMETRY_ind  *psInd  = pasWriteGeomInd + iCache;
1938         OGRGeometry *poGeometry = poFeature->GetGeometryRef();
1939         int nGType;
1940 
1941         psInd->_atomic = OCI_IND_NOTNULL;
1942 
1943         if( nSRID == -1 )
1944             psInd->sdo_srid = OCI_IND_NULL;
1945         else
1946         {
1947             psInd->sdo_srid = OCI_IND_NOTNULL;
1948             OCINumberFromInt( poSession->hError, &nSRID,
1949                               (uword)sizeof(int), OCI_NUMBER_SIGNED,
1950                               &(psGeom->sdo_srid) );
1951         }
1952 
1953         /* special more efficient case for simple points */
1954         if( wkbFlatten(poGeometry->getGeometryType()) == wkbPoint )
1955         {
1956             OGRPoint *poPoint = poGeometry->toPoint();
1957             double dfValue;
1958 
1959             psInd->sdo_point._atomic = OCI_IND_NOTNULL;
1960             psInd->sdo_elem_info = OCI_IND_NULL;
1961             psInd->sdo_ordinates = OCI_IND_NULL;
1962 
1963             dfValue = poPoint->getX();
1964             OCINumberFromReal( poSession->hError, &dfValue,
1965                                (uword)sizeof(double),
1966                                &(psGeom->sdo_point.x) );
1967 
1968             dfValue = poPoint->getY();
1969             OCINumberFromReal( poSession->hError, &dfValue,
1970                                (uword)sizeof(double),
1971                                &(psGeom->sdo_point.y) );
1972 
1973             if( nDimension == 2 )
1974             {
1975                 nGType = 2001;
1976                 psInd->sdo_point.z = OCI_IND_NULL;
1977             }
1978             else
1979             {
1980                 nGType = 3001;
1981                 psInd->sdo_point.z = OCI_IND_NOTNULL;
1982 
1983                 dfValue = poPoint->getZ();
1984                 OCINumberFromReal( poSession->hError, &dfValue,
1985                                    (uword)sizeof(double),
1986                                    &(psGeom->sdo_point.z) );
1987             }
1988         }
1989         else
1990         {
1991             psInd->sdo_point._atomic = OCI_IND_NULL;
1992             psInd->sdo_elem_info = OCI_IND_NOTNULL;
1993             psInd->sdo_ordinates = OCI_IND_NOTNULL;
1994 
1995             eErr = TranslateToSDOGeometry( poFeature->GetGeometryRef(),
1996                                            &nGType );
1997 
1998             if( eErr != OGRERR_NONE )
1999                 return eErr;
2000 
2001             /* Clear the existing eleminfo and ordinates arrays */
2002             sb4  nOldCount;
2003 
2004             OCICollSize( poSession->hEnv, poSession->hError,
2005                          psGeom->sdo_elem_info, &nOldCount );
2006             OCICollTrim( poSession->hEnv, poSession->hError,
2007                          nOldCount, psGeom->sdo_elem_info );
2008 
2009             OCICollSize( poSession->hEnv, poSession->hError,
2010                          psGeom->sdo_ordinates, &nOldCount );
2011             OCICollTrim( poSession->hEnv, poSession->hError,
2012                          nOldCount, psGeom->sdo_ordinates );
2013 
2014             // Prepare the VARRAY of element values.
2015             for (i = 0; i < nElemInfoCount; i++)
2016             {
2017                 OCINumberFromInt( poSession->hError,
2018                                   (dvoid *) (panElemInfo + i),
2019                                   (uword)sizeof(int), OCI_NUMBER_SIGNED,
2020                                   &oci_number );
2021 
2022                 OCICollAppend( poSession->hEnv, poSession->hError,
2023                                (dvoid *) &oci_number,
2024                                (dvoid *)nullptr, psGeom->sdo_elem_info );
2025             }
2026 
2027             // Prepare the VARRAY of ordinate values.
2028             for (i = 0; i < nOrdinalCount; i++)
2029             {
2030                 OCINumberFromReal( poSession->hError,
2031                                    (dvoid *) (padfOrdinals + i),
2032                                    (uword)sizeof(double), &oci_number );
2033                 OCICollAppend( poSession->hEnv, poSession->hError,
2034                                (dvoid *) &oci_number,
2035                                (dvoid *)nullptr, psGeom->sdo_ordinates );
2036             }
2037         }
2038 
2039         psInd->sdo_gtype = OCI_IND_NOTNULL;
2040         OCINumberFromInt( poSession->hError, &nGType,
2041                           (uword)sizeof(int), OCI_NUMBER_SIGNED,
2042                           &(psGeom->sdo_gtype) );
2043     }
2044     else if( pasWriteGeomInd != nullptr )
2045     {
2046         SDO_GEOMETRY_ind  *psInd  = pasWriteGeomInd + iCache;
2047         psInd->_atomic = OCI_IND_NULL;
2048         psInd->sdo_srid = OCI_IND_NULL;
2049         psInd->sdo_point._atomic = OCI_IND_NULL;
2050         psInd->sdo_elem_info = OCI_IND_NULL;
2051         psInd->sdo_ordinates = OCI_IND_NULL;
2052         psInd->sdo_gtype = OCI_IND_NULL;
2053     }
2054 
2055 /* -------------------------------------------------------------------- */
2056 /*      Set the FID.                                                    */
2057 /* -------------------------------------------------------------------- */
2058     if( poFeature->GetFID() == OGRNullFID )
2059     {
2060         if( iNextFIDToWrite < 0 )
2061         {
2062             iNextFIDToWrite = GetMaxFID() + 1;
2063         }
2064 
2065         poFeature->SetFID( iNextFIDToWrite++ );
2066     }
2067 
2068     panWriteFIDs[iCache] = static_cast<int>(poFeature->GetFID());
2069 
2070 /* -------------------------------------------------------------------- */
2071 /*      Set the other fields.                                           */
2072 /* -------------------------------------------------------------------- */
2073     for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
2074     {
2075         if( !poFeature->IsFieldSetAndNotNull( i ) )
2076         {
2077             papaeWriteFieldInd[i][iCache] = OCI_IND_NULL;
2078             continue;
2079         }
2080 
2081         papaeWriteFieldInd[i][iCache] = OCI_IND_NOTNULL;
2082 
2083         OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i);
2084 
2085         if( poFldDefn->GetType() == OFTInteger )
2086             ((int *) (papWriteFields[i]))[iCache] =
2087                 poFeature->GetFieldAsInteger( i );
2088 
2089         else if( poFldDefn->GetType() == OFTInteger64 )
2090             ((GIntBig *) (papWriteFields[i]))[iCache] =
2091                 poFeature->GetFieldAsInteger64( i );
2092 
2093         else if( poFldDefn->GetType() == OFTReal )
2094             ((double *) (papWriteFields[i]))[iCache] =
2095                 poFeature->GetFieldAsDouble( i );
2096 
2097         else
2098         {
2099             int nLen = 1;
2100             int nEachBufSize = nDefaultStringSize + 1;
2101             const char *pszStrValue = poFeature->GetFieldAsString(i);
2102 
2103             if( poFldDefn->GetType() == OFTString
2104                 && poFldDefn->GetWidth() != 0 )
2105                 nEachBufSize = poFldDefn->GetWidth() + 1;
2106 
2107             nLen = static_cast<int>(strlen(pszStrValue));
2108             if( nLen > nEachBufSize-1 )
2109                 nLen = nEachBufSize-1;
2110 
2111             char *pszTarget = ((char*)papWriteFields[i]) + iCache*nEachBufSize;
2112             strncpy( pszTarget, pszStrValue, nLen );
2113             pszTarget[nLen] = '\0';
2114         }
2115     }
2116 
2117 /* -------------------------------------------------------------------- */
2118 /*      Do we need to flush out a full set of rows?                     */
2119 /* -------------------------------------------------------------------- */
2120     nWriteCacheUsed++;
2121 
2122     if( nWriteCacheUsed == nWriteCacheMax )
2123         return FlushPendingFeatures();
2124     else
2125         return OGRERR_NONE;
2126 }
2127 
2128 /************************************************************************/
2129 /*                        FlushPendingFeatures()                        */
2130 /************************************************************************/
2131 
FlushPendingFeatures()2132 OGRErr OGROCITableLayer::FlushPendingFeatures()
2133 
2134 {
2135     OGROCISession      *poSession = poDS->GetSession();
2136 
2137     if( nWriteCacheUsed > 0 )
2138     {
2139         CPLDebug( "OCI", "Flushing %d features on layer %s",
2140                   nWriteCacheUsed, poFeatureDefn->GetName() );
2141 
2142         if( poSession->Failed(
2143                 OCIStmtExecute( poSession->hSvcCtx,
2144                                 poBoundStatement->GetStatement(),
2145                                 poSession->hError, (ub4) nWriteCacheUsed,
2146                                 (ub4) 0,
2147                                 (OCISnapshot *)nullptr, (OCISnapshot *)nullptr,
2148                                 (ub4) OCI_COMMIT_ON_SUCCESS ),
2149                 "OCIStmtExecute" ) )
2150         {
2151             nWriteCacheUsed = 0;
2152             return OGRERR_FAILURE;
2153         }
2154         else
2155         {
2156             nWriteCacheUsed = 0;
2157             return OGRERR_NONE;
2158         }
2159     }
2160     else
2161         return OGRERR_NONE;
2162 }
2163 
2164 /************************************************************************/
2165 /*                             SyncToDisk()                             */
2166 /*                                                                      */
2167 /*      Perhaps we should also be putting the metadata into a           */
2168 /*      usable state?                                                   */
2169 /************************************************************************/
2170 
SyncToDisk()2171 OGRErr OGROCITableLayer::SyncToDisk()
2172 
2173 {
2174     OGRErr eErr = FlushPendingFeatures();
2175 
2176     UpdateLayerExtents();
2177 
2178     CreateSpatialIndex();
2179 
2180     bNewLayer = FALSE;
2181 
2182     return eErr;
2183 }
2184 
2185 /*************************************************************************/
2186 /*                         CreateSpatialIndex()                          */
2187 /*************************************************************************/
2188 
CreateSpatialIndex()2189 void OGROCITableLayer::CreateSpatialIndex()
2190 
2191 {
2192 /* -------------------------------------------------------------------- */
2193 /*      For new layers we try to create a spatial index.                */
2194 /* -------------------------------------------------------------------- */
2195     if( bNewLayer && sExtent.IsInit() )
2196     {
2197 /* -------------------------------------------------------------------- */
2198 /*      If the user has disabled INDEX support then don't create the    */
2199 /*      index.                                                          */
2200 /* -------------------------------------------------------------------- */
2201         if( !CPLFetchBool( papszOptions, "SPATIAL_INDEX", true ) ||
2202             !CPLFetchBool( papszOptions, "INDEX", true ) )
2203             return;
2204 
2205 /* -------------------------------------------------------------------- */
2206 /*      Establish an index name.  For some reason Oracle 8.1.7 does     */
2207 /*      not support spatial index names longer than 18 characters so    */
2208 /*      we magic up an index name if it would be too long.              */
2209 /* -------------------------------------------------------------------- */
2210         char  szIndexName[20];
2211 
2212         if( strlen(poFeatureDefn->GetName()) < 15 )
2213             snprintf( szIndexName, sizeof(szIndexName), "%s_idx", poFeatureDefn->GetName() );
2214         else if( strlen(poFeatureDefn->GetName()) < 17 )
2215             snprintf( szIndexName, sizeof(szIndexName), "%si", poFeatureDefn->GetName() );
2216         else
2217         {
2218             int i, nHash = 0;
2219             const char *pszSrcName = poFeatureDefn->GetName();
2220 
2221             for( i = 0; pszSrcName[i] != '\0'; i++ )
2222                 nHash = (nHash + i * pszSrcName[i]) % 987651;
2223 
2224             snprintf( szIndexName, sizeof(szIndexName), "OSI_%d", nHash );
2225         }
2226 
2227         poDS->GetSession()->CleanName( szIndexName );
2228 
2229 /* -------------------------------------------------------------------- */
2230 /*      Try creating an index on the table now.  Use a simple 5         */
2231 /*      level quadtree based index.  Would R-tree be a better default?  */
2232 /* -------------------------------------------------------------------- */
2233         OGROCIStringBuf  sIndexCmd;
2234         OGROCIStatement oExecStatement( poDS->GetSession() );
2235 
2236         sIndexCmd.Appendf( 10000, "CREATE INDEX \"%s\" ON %s(\"%s\") "
2237                            "INDEXTYPE IS MDSYS.SPATIAL_INDEX ",
2238                            szIndexName,
2239                            poFeatureDefn->GetName(),
2240                            pszGeomName );
2241 
2242         int bAddLayerGType = CPLTestBool(
2243             CSLFetchNameValueDef( papszOptions, "ADD_LAYER_GTYPE", "YES") ) &&
2244             GetGeomType() != wkbUnknown;
2245 
2246         CPLString osParams(CSLFetchNameValueDef(papszOptions,"INDEX_PARAMETERS", ""));
2247         if( bAddLayerGType || !osParams.empty() )
2248         {
2249             sIndexCmd.Append( " PARAMETERS( '" );
2250             if( !osParams.empty() )
2251                 sIndexCmd.Append( osParams.c_str() );
2252             if( bAddLayerGType &&
2253                 osParams.ifind("LAYER_GTYPE") == std::string::npos )
2254             {
2255                 if( !osParams.empty() )
2256                     sIndexCmd.Append( ", " );
2257                 sIndexCmd.Append( "LAYER_GTYPE=" );
2258                 if( wkbFlatten(GetGeomType()) == wkbPoint )
2259                     sIndexCmd.Append( "POINT" );
2260                 else if( wkbFlatten(GetGeomType()) == wkbLineString )
2261                     sIndexCmd.Append( "LINE" );
2262                 else if( wkbFlatten(GetGeomType()) == wkbPolygon )
2263                     sIndexCmd.Append( "POLYGON" );
2264                 else if( wkbFlatten(GetGeomType()) == wkbMultiPoint )
2265                     sIndexCmd.Append( "MULTIPOINT" );
2266                 else if( wkbFlatten(GetGeomType()) == wkbMultiLineString )
2267                     sIndexCmd.Append( "MULTILINE" );
2268                 else if( wkbFlatten(GetGeomType()) == wkbMultiPolygon )
2269                     sIndexCmd.Append( "MULTIPOLYGON" );
2270                 else
2271                     sIndexCmd.Append( "COLLECTION" );
2272             }
2273             sIndexCmd.Append( "' )" );
2274         }
2275 
2276         if( oExecStatement.Execute( sIndexCmd.GetString() ) != CE_None )
2277         {
2278             CPLString osDropCommand;
2279             osDropCommand.Printf( "DROP INDEX \"%s\"", szIndexName );
2280             oExecStatement.Execute( osDropCommand );
2281         }
2282     }
2283 }
2284 
GetMaxFID()2285 int OGROCITableLayer::GetMaxFID()
2286 {
2287     if( nFirstId > 0 )
2288         return nFirstId - 1;
2289 
2290     if( pszFIDName == nullptr )
2291         return 0;
2292 
2293     OGROCIStringBuf sCmd;
2294     OGROCIStatement oSelect( poDS->GetSession() );
2295 
2296     sCmd.Appendf( 10000, "SELECT MAX(\"%s\") FROM \"%s\"",
2297             pszFIDName,
2298             poFeatureDefn->GetName()
2299             );
2300 
2301     oSelect.Execute( sCmd.GetString() );
2302 
2303     char **papszResult = oSelect.SimpleFetchRow();
2304     return CSLCount(papszResult) == 1 ? atoi( papszResult[0] ) : 0;
2305 }
2306