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