1 /******************************************************************************
2  *
3  * Project:  Oracle Spatial Driver
4  * Purpose:  Implementation of the OGROCILayer class.  This is layer semantics
5  *           shared between table accessors and ExecuteSQL() result
6  *           pseudo-layers.
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 
34 CPL_CVSID("$Id: ogrocilayer.cpp 22f8ae3bf7bc3cccd970992655c63fc5254d3206 2018-04-08 20:13:05 +0200 Even Rouault $")
35 
36 /************************************************************************/
37 /*                           OGROCILayer()                               */
38 /************************************************************************/
39 
OGROCILayer()40 OGROCILayer::OGROCILayer()
41 
42 {
43     poFeatureDefn = nullptr;
44     poDS = nullptr;
45     poStatement = nullptr;
46 
47     pszQueryStatement = nullptr;
48     nResultOffset = 0;
49     pszGeomName = nullptr;
50     iGeomColumn = -1;
51     pszFIDName = nullptr;
52     iFIDColumn = -1;
53 
54     hLastGeom = nullptr;
55     hLastGeomInd = nullptr;
56 
57     iNextShapeId = 0;
58 }
59 
60 /************************************************************************/
61 /*                            ~OGROCILayer()                             */
62 /************************************************************************/
63 
~OGROCILayer()64 OGROCILayer::~OGROCILayer()
65 
66 {
67     if( m_nFeaturesRead > 0 && poFeatureDefn != nullptr )
68     {
69         CPLDebug( "OCI", "%d features read on layer '%s'.",
70                   (int) m_nFeaturesRead,
71                   poFeatureDefn->GetName() );
72     }
73 
74     OGROCILayer::ResetReading();
75 
76     CPLFree( pszGeomName );
77     pszGeomName = nullptr;
78 
79     CPLFree( pszFIDName );
80     pszFIDName = nullptr;
81 
82     CPLFree( pszQueryStatement );
83     pszQueryStatement = nullptr;
84 
85     if( poFeatureDefn != nullptr )
86         poFeatureDefn->Release();
87 }
88 
89 /************************************************************************/
90 /*                            ResetReading()                            */
91 /************************************************************************/
92 
ResetReading()93 void OGROCILayer::ResetReading()
94 
95 {
96     if( poStatement != nullptr )
97         delete poStatement;
98     poStatement = nullptr;
99 
100     iNextShapeId = 0;
101 }
102 
103 /************************************************************************/
104 /*                           GetNextFeature()                           */
105 /*                                                                      */
106 /*      By default we implement the full spatial and attribute query    */
107 /*      semantics manually here.  The table query class will            */
108 /*      override this method and implement these inline, but the        */
109 /*      simple SELECT statement evaluator (OGROCISelectLayer) will      */
110 /*      depend us this code implementing additional spatial or          */
111 /*      attribute query semantics.                                      */
112 /************************************************************************/
113 
GetNextFeature()114 OGRFeature *OGROCILayer::GetNextFeature()
115 
116 {
117     while( true )
118     {
119         OGRFeature      *poFeature;
120 
121         poFeature = GetNextRawFeature();
122         if( poFeature == nullptr )
123             return nullptr;
124 
125         if( (m_poFilterGeom == nullptr
126             || FilterGeometry( poFeature->GetGeometryRef() ) )
127             && (m_poAttrQuery == nullptr
128                 || m_poAttrQuery->Evaluate( poFeature )) )
129             return poFeature;
130 
131         delete poFeature;
132     }
133 }
134 
135 /************************************************************************/
136 /*                         GetNextRawFeature()                          */
137 /************************************************************************/
138 
GetNextRawFeature()139 OGRFeature *OGROCILayer::GetNextRawFeature()
140 
141 {
142 /* -------------------------------------------------------------------- */
143 /*      Do we need to establish an initial query?                       */
144 /* -------------------------------------------------------------------- */
145     if( iNextShapeId == 0 && poStatement == nullptr )
146     {
147         if( !ExecuteQuery(pszQueryStatement) )
148             return nullptr;
149     }
150 
151 /* -------------------------------------------------------------------- */
152 /*      Have we run out of query results, such that we have no          */
153 /*      statement left?                                                 */
154 /* -------------------------------------------------------------------- */
155     if( poStatement == nullptr )
156         return nullptr;
157 
158 /* -------------------------------------------------------------------- */
159 /*      Are we in some sort of error condition?                         */
160 /* -------------------------------------------------------------------- */
161     hLastGeom = nullptr;
162 
163     char **papszResult = poStatement->SimpleFetchRow();
164 
165     if( papszResult == nullptr )
166     {
167         iNextShapeId = MAX(1,iNextShapeId);
168         delete poStatement;
169         poStatement = nullptr;
170         return nullptr;
171     }
172 
173 /* -------------------------------------------------------------------- */
174 /*      Create a feature from the current result.                       */
175 /* -------------------------------------------------------------------- */
176     int         iField;
177     OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
178 
179     poFeature->SetFID( iNextShapeId );
180     iNextShapeId++;
181     m_nFeaturesRead++;
182 
183     if( iFIDColumn != -1 && papszResult[iFIDColumn] != nullptr )
184         poFeature->SetFID( atoi(papszResult[iFIDColumn]) );
185 
186     for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
187     {
188         if( papszResult[iField] != nullptr )
189             poFeature->SetField( iField, papszResult[iField] );
190         else
191             poFeature->SetFieldNull( iField );
192     }
193 
194 /* -------------------------------------------------------------------- */
195 /*      Translate geometry if we have it.                               */
196 /* -------------------------------------------------------------------- */
197     if( iGeomColumn != -1 )
198     {
199         poFeature->SetGeometryDirectly( TranslateGeometry() );
200 
201         OGROCISession      *poSession = poDS->GetSession();
202 
203         if( poFeature->GetGeometryRef() != nullptr && hLastGeom != nullptr )
204             poSession->Failed(
205                 OCIObjectFree(poSession->hEnv, poSession->hError,
206                               (dvoid *) hLastGeom,
207                               (ub2)OCI_OBJECTFREE_FORCE) );
208 
209         hLastGeom = nullptr;
210         hLastGeomInd = nullptr;
211     }
212 
213     nResultOffset++;
214 
215     return poFeature;
216 }
217 
218 /************************************************************************/
219 /*                            ExecuteQuery()                            */
220 /*                                                                      */
221 /*      This is invoke when the first request for a feature is          */
222 /*      made.  It executes the query, and binds columns as needed.      */
223 /*      The OGROCIStatement is used for most of the work.               */
224 /************************************************************************/
225 
ExecuteQuery(const char * pszReqQuery)226 int OGROCILayer::ExecuteQuery( const char *pszReqQuery )
227 
228 {
229     OGROCISession      *poSession = poDS->GetSession();
230 
231     CPLAssert( pszReqQuery != nullptr );
232     CPLAssert( poStatement == nullptr );
233 
234 /* -------------------------------------------------------------------- */
235 /*      Execute the query.                                              */
236 /* -------------------------------------------------------------------- */
237     poStatement = new OGROCIStatement( poSession );
238     if( poStatement->Execute( pszReqQuery ) != CE_None )
239     {
240         delete poStatement;
241         poStatement = nullptr;
242         return FALSE;
243     }
244     nResultOffset = 0;
245 
246 /* -------------------------------------------------------------------- */
247 /*      Do additional work binding the geometry column.                 */
248 /* -------------------------------------------------------------------- */
249     if( iGeomColumn != -1 )
250     {
251         OCIDefine *hGDefine = nullptr;
252 
253         if( poSession->Failed(
254             OCIDefineByPos(poStatement->GetStatement(), &hGDefine,
255                            poSession->hError,
256                            (ub4) iGeomColumn+1, (dvoid *)nullptr, (sb4)0, SQLT_NTY,
257                            (dvoid *)nullptr, (ub2 *)nullptr, (ub2 *)nullptr, (ub4)OCI_DEFAULT),
258             "OCIDefineByPos(geometry)") )
259             return FALSE;
260 
261         if( poSession->Failed(
262             OCIDefineObject(hGDefine, poSession->hError,
263                             poSession->hGeometryTDO,
264                             (dvoid **) &hLastGeom, (ub4 *)nullptr,
265                             (dvoid **) &hLastGeomInd, (ub4 *)nullptr ),
266             "OCIDefineObject") )
267             return FALSE;
268     }
269 
270     return TRUE;
271 }
272 
273 /************************************************************************/
274 /*                         TranslateGeometry()                          */
275 /************************************************************************/
276 
TranslateGeometry()277 OGRGeometry *OGROCILayer::TranslateGeometry()
278 
279 {
280     OGROCISession      *poSession = poDS->GetSession();
281 
282 /* -------------------------------------------------------------------- */
283 /*      Is the geometry NULL?                                           */
284 /* -------------------------------------------------------------------- */
285     if( hLastGeom == nullptr || hLastGeomInd == nullptr
286         || hLastGeomInd->_atomic == OCI_IND_NULL )
287         return nullptr;
288 
289 /* -------------------------------------------------------------------- */
290 /*      Get the size of the sdo_elem_info and sdo_ordinates arrays.     */
291 /* -------------------------------------------------------------------- */
292     int nElemCount, nOrdCount;
293 
294     if( poSession->Failed(
295         OCICollSize( poSession->hEnv, poSession->hError,
296                      (OCIColl *)(hLastGeom->sdo_elem_info), &nElemCount),
297         "OCICollSize(sdo_elem_info)" ) )
298         return nullptr;
299 
300     if( poSession->Failed(
301         OCICollSize( poSession->hEnv, poSession->hError,
302                      (OCIColl *)(hLastGeom->sdo_ordinates), &nOrdCount),
303         "OCICollSize(sdo_ordinates)" ) )
304         return nullptr;
305 
306 /* -------------------------------------------------------------------- */
307 /*      Get the GType.                                                  */
308 /* -------------------------------------------------------------------- */
309     int nGType;
310 
311     if( poSession->Failed(
312         OCINumberToInt(poSession->hError, &(hLastGeom->sdo_gtype),
313                        (uword)sizeof(int), OCI_NUMBER_SIGNED,
314                        (dvoid *)&nGType),
315         "OCINumberToInt(GType)" ) )
316         return nullptr;
317 
318 /* -------------------------------------------------------------------- */
319 /*      Establish the dimension.                                        */
320 /* -------------------------------------------------------------------- */
321     int nDimension = MAX(2,(nGType / 1000));
322 
323 /* -------------------------------------------------------------------- */
324 /*      Handle point data directly from built-in point info.            */
325 /* -------------------------------------------------------------------- */
326     if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_POINT)
327         && hLastGeomInd->sdo_point._atomic == OCI_IND_NOTNULL
328         && hLastGeomInd->sdo_point.x == OCI_IND_NOTNULL
329         && hLastGeomInd->sdo_point.y == OCI_IND_NOTNULL )
330     {
331         double     dfX, dfY, dfZ = 0.0;
332 
333         OCINumberToReal(poSession->hError, &(hLastGeom->sdo_point.x),
334                         (uword)sizeof(double), (dvoid *)&dfX);
335         OCINumberToReal(poSession->hError, &(hLastGeom->sdo_point.y),
336                         (uword)sizeof(double), (dvoid *)&dfY);
337         if( hLastGeomInd->sdo_point.z == OCI_IND_NOTNULL )
338             OCINumberToReal(poSession->hError, &(hLastGeom->sdo_point.z),
339                             (uword)sizeof(double), (dvoid *)&dfZ);
340 
341         if( nDimension == 3 )
342             return new OGRPoint( dfX, dfY, dfZ );
343         else
344             return new OGRPoint( dfX, dfY );
345     }
346 
347 /* -------------------------------------------------------------------- */
348 /*      If this is a sort of container geometry, create the             */
349 /*      container now.                                                  */
350 /* -------------------------------------------------------------------- */
351     OGRGeometryCollection *poCollection = nullptr;
352     OGRPolygon *poPolygon = nullptr;
353 
354     if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_POLYGON) )
355         poPolygon = new OGRPolygon();
356     else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_COLLECTION) )
357         poCollection = new OGRGeometryCollection();
358     else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_MULTIPOINT) )
359         poCollection = new OGRMultiPoint();
360     else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_MULTILINESTRING) )
361         poCollection = new OGRMultiLineString();
362     else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_MULTIPOLYGON) )
363         poCollection = new OGRMultiPolygon();
364 
365 /* ==================================================================== */
366 /*      Loop over the component elements.                               */
367 /* ==================================================================== */
368     for( int iElement = 0; iElement < nElemCount; iElement += 3 )
369     {
370         int       nInterpretation, nEType;
371         int       nStartOrdinal, nElemOrdCount;
372 
373         LoadElementInfo( iElement, nElemCount, nOrdCount,
374                          &nEType, &nInterpretation,
375                          &nStartOrdinal, &nElemOrdCount );
376 
377 /* -------------------------------------------------------------------- */
378 /*      Translate this element.                                         */
379 /* -------------------------------------------------------------------- */
380         OGRGeometry *poGeom;
381 
382         poGeom = TranslateGeometryElement( &iElement, nGType, nDimension,
383                                            nEType, nInterpretation,
384                                            nStartOrdinal - 1, nElemOrdCount );
385 
386         if( poGeom == nullptr )
387             return nullptr;
388 
389 /* -------------------------------------------------------------------- */
390 /*      Based on GType do what is appropriate.                          */
391 /* -------------------------------------------------------------------- */
392         if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_LINESTRING) )
393         {
394             CPLAssert(wkbFlatten(poGeom->getGeometryType()) == wkbLineString);
395             return poGeom;
396         }
397 
398         else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_POINT) )
399         {
400             CPLAssert(wkbFlatten(poGeom->getGeometryType()) == wkbPoint);
401             return poGeom;
402         }
403 
404         else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_POLYGON) )
405         {
406             CPLAssert(wkbFlatten(poGeom->getGeometryType()) == wkbLineString );
407             poPolygon->addRingDirectly( poGeom->toLinearRing() );
408         }
409         else
410         {
411             CPLAssert( poCollection != nullptr );
412             if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint )
413             {
414                 OGRMultiPoint *poMP = poGeom->toMultiPoint();
415                 for( auto&& poPoint: *poMP )
416                     poCollection->addGeometry(poPoint);
417                 delete poMP;
418             }
419             else if( nEType % 1000 == 3 )
420             {
421                 /* its one poly ring, create new poly or add to existing */
422                 if( nEType == 1003 )
423                 {
424                     if( poPolygon != nullptr
425                         && poPolygon->getExteriorRing() != nullptr )
426                     {
427                         poCollection->addGeometryDirectly( poPolygon );
428                         poPolygon = nullptr;
429                     }
430 
431                     poPolygon = new OGRPolygon();
432                 }
433 
434                 if( poPolygon != nullptr )
435                     poPolygon->addRingDirectly( poGeom->toLinearRing() );
436                 else
437                 {
438                     CPLAssert( poPolygon != nullptr );
439                 }
440             }
441             else
442                 poCollection->addGeometryDirectly( poGeom );
443         }
444     }
445 
446     if( poCollection != nullptr
447         && poPolygon != nullptr )
448         poCollection->addGeometryDirectly( poPolygon );
449 
450 /* -------------------------------------------------------------------- */
451 /*      Return resulting collection geometry.                           */
452 /* -------------------------------------------------------------------- */
453     if( poCollection == nullptr )
454         return poPolygon;
455     else
456         return poCollection;
457 }
458 
459 /************************************************************************/
460 /*                          LoadElementInfo()                           */
461 /*                                                                      */
462 /*      Fetch the start ordinal, count, EType and interpretation        */
463 /*      values for a particular element.                                */
464 /************************************************************************/
465 
466 int
LoadElementInfo(int iElement,int nElemCount,int nTotalOrdCount,int * pnEType,int * pnInterpretation,int * pnStartOrdinal,int * pnElemOrdCount)467 OGROCILayer::LoadElementInfo( int iElement, int nElemCount, int nTotalOrdCount,
468                               int *pnEType, int *pnInterpretation,
469                               int *pnStartOrdinal, int *pnElemOrdCount )
470 
471 {
472     OGROCISession      *poSession = poDS->GetSession();
473     boolean bExists;
474     OCINumber *hNumber;
475 /* -------------------------------------------------------------------- */
476 /*      Get the details about element from the elem_info array.         */
477 /* -------------------------------------------------------------------- */
478     OCICollGetElem(poSession->hEnv, poSession->hError,
479                    (OCIColl *)(hLastGeom->sdo_elem_info),
480                    (sb4)(iElement+0), (boolean *)&bExists,
481                    (dvoid **)&hNumber, nullptr );
482     OCINumberToInt(poSession->hError, hNumber, (uword)sizeof(ub4),
483                    OCI_NUMBER_UNSIGNED, (dvoid *) pnStartOrdinal );
484 
485     OCICollGetElem(poSession->hEnv, poSession->hError,
486                    (OCIColl *)(hLastGeom->sdo_elem_info),
487                    (sb4)(iElement+1), (boolean *)&bExists,
488                    (dvoid **)&hNumber, nullptr );
489     OCINumberToInt(poSession->hError, hNumber, (uword)sizeof(ub4),
490                    OCI_NUMBER_UNSIGNED, (dvoid *) pnEType );
491 
492     OCICollGetElem(poSession->hEnv, poSession->hError,
493                    (OCIColl *)(hLastGeom->sdo_elem_info),
494                    (sb4)(iElement+2), (boolean *)&bExists,
495                    (dvoid **)&hNumber, nullptr );
496     OCINumberToInt(poSession->hError, hNumber, (uword)sizeof(ub4),
497                    OCI_NUMBER_UNSIGNED, (dvoid *) pnInterpretation );
498 
499     if( iElement < nElemCount-3 )
500     {
501         ub4 nNextStartOrdinal;
502 
503         OCICollGetElem(poSession->hEnv, poSession->hError,
504                        (OCIColl *)(hLastGeom->sdo_elem_info),
505                        (sb4)(iElement+3), (boolean *)&bExists,
506                        (dvoid **)&hNumber,nullptr);
507         OCINumberToInt(poSession->hError, hNumber, (uword)sizeof(ub4),
508                        OCI_NUMBER_UNSIGNED, (dvoid *) &nNextStartOrdinal );
509 
510         *pnElemOrdCount = nNextStartOrdinal - *pnStartOrdinal;
511     }
512     else
513         *pnElemOrdCount = nTotalOrdCount - *pnStartOrdinal + 1;
514 
515     return TRUE;
516 }
517 
518 /************************************************************************/
519 /*                      TranslateGeometryElement()                      */
520 /************************************************************************/
521 
522 OGRGeometry *
TranslateGeometryElement(int * piElement,int nGType,int nDimension,int nEType,int nInterpretation,int nStartOrdinal,int nElemOrdCount)523 OGROCILayer::TranslateGeometryElement( int *piElement,
524                                        int nGType, int nDimension,
525                                        int nEType, int nInterpretation,
526                                        int nStartOrdinal, int nElemOrdCount )
527 
528 {
529 /* -------------------------------------------------------------------- */
530 /*      Handle simple point.                                            */
531 /* -------------------------------------------------------------------- */
532     if( nEType == 1 && nInterpretation == 1 )
533     {
534         OGRPoint *poPoint = new OGRPoint();
535         double dfX, dfY, dfZ = 0.0;
536 
537         GetOrdinalPoint( nStartOrdinal, nDimension, &dfX, &dfY, &dfZ );
538 
539         poPoint->setX( dfX );
540         poPoint->setY( dfY );
541         if( nDimension == 3 )
542             poPoint->setZ( dfZ );
543 
544         return poPoint;
545     }
546 
547 /* -------------------------------------------------------------------- */
548 /*      Handle multipoint.                                              */
549 /* -------------------------------------------------------------------- */
550     else if( nEType == 1 && nInterpretation > 1 )
551     {
552         OGRMultiPoint *poMP = new OGRMultiPoint();
553         double dfX, dfY, dfZ = 0.0;
554         int i;
555 
556         CPLAssert( nInterpretation == nElemOrdCount / nDimension );
557 
558         for( i = 0; i < nInterpretation; i++ )
559         {
560             GetOrdinalPoint( nStartOrdinal + i*nDimension, nDimension,
561                              &dfX, &dfY, &dfZ );
562 
563             OGRPoint *poPoint = (nDimension == 3) ? new OGRPoint( dfX, dfY, dfZ ):  new OGRPoint( dfX, dfY );
564             poMP->addGeometryDirectly( poPoint );
565         }
566         return poMP;
567     }
568 
569 /* -------------------------------------------------------------------- */
570 /*      Discard orientations for oriented points.                       */
571 /* -------------------------------------------------------------------- */
572     else if( nEType == 1 && nInterpretation == 0 )
573     {
574         CPLDebug( "OCI", "Ignoring orientations for oriented points." );
575         return nullptr;
576     }
577 
578 /* -------------------------------------------------------------------- */
579 /*      Handle line strings consisting of straight segments.            */
580 /* -------------------------------------------------------------------- */
581     else if( nEType == 2 && nInterpretation == 1 )
582     {
583         OGRLineString *poLS = new OGRLineString();
584         int nPointCount = nElemOrdCount / nDimension, i;
585 
586         poLS->setNumPoints( nPointCount );
587 
588         for( i = 0; i < nPointCount; i++ )
589         {
590             double dfX, dfY, dfZ = 0.0;
591 
592             GetOrdinalPoint( i*nDimension + nStartOrdinal, nDimension,
593                              &dfX, &dfY, &dfZ );
594             if (nDimension == 3)
595                 poLS->setPoint( i, dfX, dfY, dfZ );
596             else
597                 poLS->setPoint( i, dfX, dfY );
598         }
599 
600         return poLS;
601     }
602 
603 /* -------------------------------------------------------------------- */
604 /*      Handle line strings consisting of circular arcs.                */
605 /* -------------------------------------------------------------------- */
606     else if( nEType == 2 && nInterpretation == 2 )
607     {
608         OGRLineString *poLS = new OGRLineString();
609         int nPointCount = nElemOrdCount / nDimension, i;
610 
611         for( i = 0; i < nPointCount-2; i += 2 )
612         {
613             double dfStartX, dfStartY, dfStartZ = 0.0;
614             double dfMidX, dfMidY, dfMidZ = 0.0;
615             double dfEndX, dfEndY, dfEndZ = 0.0;
616 
617             GetOrdinalPoint( i*nDimension + nStartOrdinal, nDimension,
618                              &dfStartX, &dfStartY, &dfStartZ );
619             GetOrdinalPoint( (i+1)*nDimension + nStartOrdinal, nDimension,
620                              &dfMidX, &dfMidY, &dfMidZ );
621             GetOrdinalPoint( (i+2)*nDimension + nStartOrdinal, nDimension,
622                              &dfEndX, &dfEndY, &dfEndZ );
623 
624             OGROCIStrokeArcToOGRGeometry_Points( dfStartX, dfStartY,
625                                                  dfMidX, dfMidY,
626                                                  dfEndX, dfEndY,
627                                                  6.0, FALSE, poLS );
628         }
629 
630         return poLS;
631     }
632 
633 /* -------------------------------------------------------------------- */
634 /*      Handle polygon rings.  Treat curves as if they were             */
635 /*      linestrings.                                                    */
636 /* -------------------------------------------------------------------- */
637     else if( nEType % 1000 == 3 && nInterpretation == 1 )
638     {
639         OGRLinearRing *poLS = new OGRLinearRing();
640         int nPointCount = nElemOrdCount / nDimension, i;
641 
642         poLS->setNumPoints( nPointCount );
643 
644         for( i = 0; i < nPointCount; i++ )
645         {
646             double dfX, dfY, dfZ = 0.0;
647 
648             GetOrdinalPoint( i*nDimension + nStartOrdinal, nDimension,
649                              &dfX, &dfY, &dfZ );
650             if (nDimension == 3)
651                 poLS->setPoint( i, dfX, dfY, dfZ );
652             else
653                 poLS->setPoint( i, dfX, dfY );
654         }
655 
656         return poLS;
657     }
658 
659 /* -------------------------------------------------------------------- */
660 /*      Handle polygon rings made of circular arcs.                     */
661 /* -------------------------------------------------------------------- */
662     else if( nEType % 1000 == 3 && nInterpretation == 2 )
663     {
664         OGRLineString *poLS = new OGRLinearRing();
665         int nPointCount = nElemOrdCount / nDimension, i;
666 
667         for( i = 0; i < nPointCount-2; i += 2 )
668         {
669             double dfStartX, dfStartY, dfStartZ = 0.0;
670             double dfMidX, dfMidY, dfMidZ = 0.0;
671             double dfEndX, dfEndY, dfEndZ = 0.0;
672 
673             GetOrdinalPoint( i*nDimension + nStartOrdinal, nDimension,
674                              &dfStartX, &dfStartY, &dfStartZ );
675             GetOrdinalPoint( (i+1)*nDimension + nStartOrdinal, nDimension,
676                              &dfMidX, &dfMidY, &dfMidZ );
677             GetOrdinalPoint( (i+2)*nDimension + nStartOrdinal, nDimension,
678                              &dfEndX, &dfEndY, &dfEndZ );
679 
680             OGROCIStrokeArcToOGRGeometry_Points( dfStartX, dfStartY,
681                                                  dfMidX, dfMidY,
682                                                  dfEndX, dfEndY,
683                                                  6.0, FALSE, poLS );
684         }
685 
686         return poLS;
687     }
688 
689 /* -------------------------------------------------------------------- */
690 /*      Handle rectangle definitions ... translate into a linear ring.  */
691 /* -------------------------------------------------------------------- */
692     else if( nEType % 1000 == 3 && nInterpretation == 3 )
693     {
694         OGRLinearRing *poLS = new OGRLinearRing();
695         double dfX1, dfY1, dfZ1 = 0.0;
696         double dfX2, dfY2, dfZ2 = 0.0;
697 
698         GetOrdinalPoint( nStartOrdinal, nDimension,
699                          &dfX1, &dfY1, &dfZ1 );
700         GetOrdinalPoint( nStartOrdinal + nDimension, nDimension,
701                          &dfX2, &dfY2, &dfZ2 );
702 
703         poLS->setNumPoints( 5 );
704 
705         poLS->setPoint( 0, dfX1, dfY1, dfZ1 );
706         poLS->setPoint( 1, dfX2, dfY1, dfZ1 );
707         poLS->setPoint( 2, dfX2, dfY2, dfZ2 );
708         poLS->setPoint( 3, dfX1, dfY2, dfZ2 );
709         poLS->setPoint( 4, dfX1, dfY1, dfZ1 );
710 
711         return poLS;
712     }
713 
714 /* -------------------------------------------------------------------- */
715 /*      Handle circle definitions ... translate into a linear ring.     */
716 /* -------------------------------------------------------------------- */
717     else if( nEType % 100 == 3 && nInterpretation == 4 )
718     {
719         OGRLinearRing *poLS = new OGRLinearRing();
720         double dfX1, dfY1, dfZ1 = 0.0;
721         double dfX2, dfY2, dfZ2 = 0.0;
722         double dfX3, dfY3, dfZ3 = 0.0;
723 
724         GetOrdinalPoint( nStartOrdinal, nDimension,
725                          &dfX1, &dfY1, &dfZ1 );
726         GetOrdinalPoint( nStartOrdinal + nDimension, nDimension,
727                          &dfX2, &dfY2, &dfZ2 );
728         GetOrdinalPoint( nStartOrdinal + nDimension*2, nDimension,
729                          &dfX3, &dfY3, &dfZ3 );
730 
731         OGROCIStrokeArcToOGRGeometry_Points( dfX1, dfY1,
732                                              dfX2, dfY2,
733                                              dfX3, dfY3,
734                                              6.0, TRUE, poLS );
735 
736         return poLS;
737     }
738 
739 /* -------------------------------------------------------------------- */
740 /*      Handle compound line strings and polygon rings.                 */
741 /*                                                                      */
742 /*      This is quite complicated since we need to consume several      */
743 /*      following elements, and merge the resulting geometries.         */
744 /* -------------------------------------------------------------------- */
745     else if( nEType == 4  || nEType % 100 == 5 )
746     {
747         int nSubElementCount = nInterpretation;
748         OGRLineString *poLS;
749         int nElemCount, nTotalOrdCount;
750         OGROCISession      *poSession = poDS->GetSession();
751 
752         if( poSession->Failed(
753             OCICollSize( poSession->hEnv, poSession->hError,
754                          (OCIColl *)(hLastGeom->sdo_elem_info), &nElemCount),
755             "OCICollSize(sdo_elem_info)" ) )
756             return nullptr;
757 
758         if( poSession->Failed(
759             OCICollSize( poSession->hEnv, poSession->hError,
760                          (OCIColl*)(hLastGeom->sdo_ordinates),&nTotalOrdCount),
761             "OCICollSize(sdo_ordinates)" ) )
762             return nullptr;
763 
764         if( nEType == 4 )
765             poLS = new OGRLineString();
766         else
767             poLS = new OGRLinearRing();
768 
769         for( *piElement += 3; nSubElementCount-- > 0;  *piElement += 3 )
770         {
771             LoadElementInfo( *piElement, nElemCount, nTotalOrdCount,
772                              &nEType, &nInterpretation,
773                              &nStartOrdinal, &nElemOrdCount );
774 
775             // Adjust for repeated end point except for last element.
776             if( nSubElementCount > 0 )
777                 nElemOrdCount += nDimension;
778 
779             // translate element.
780             OGRGeometry* poGeom =
781                 TranslateGeometryElement( piElement, nGType, nDimension,
782                                           nEType, nInterpretation,
783                                           nStartOrdinal - 1, nElemOrdCount );
784             OGRLineString* poElemLS = dynamic_cast<OGRLineString *>(poGeom);
785 
786             // Try to append to our aggregate linestring/ring
787             if( poElemLS )
788             {
789                 if( poLS->getNumPoints() > 0 )
790                 {
791                     CPLAssert(
792                         poElemLS->getX(0) == poLS->getX(poLS->getNumPoints()-1)
793                         && poElemLS->getY(0) ==poLS->getY(poLS->getNumPoints()-1));
794 
795                     poLS->addSubLineString( poElemLS, 1 );
796                 }
797                 else
798                     poLS->addSubLineString( poElemLS, 0 );
799             }
800             delete poGeom;
801         }
802 
803         *piElement -= 3;
804         return poLS;
805     }
806 
807 /* -------------------------------------------------------------------- */
808 /*      Otherwise it is apparently unsupported.                         */
809 /* -------------------------------------------------------------------- */
810     else
811     {
812 
813         CPLDebug( "OCI", "Geometry with EType=%d, Interp=%d ignored.",
814                   nEType, nInterpretation );
815     }
816 
817     return nullptr;
818 }
819 
820 /************************************************************************/
821 /*                          GetOrdinalPoint()                           */
822 /************************************************************************/
823 
GetOrdinalPoint(int iOrdinal,int nDimension,double * pdfX,double * pdfY,double * pdfZ)824 int OGROCILayer::GetOrdinalPoint( int iOrdinal, int nDimension,
825                                   double *pdfX, double *pdfY, double *pdfZ )
826 
827 {
828     OGROCISession      *poSession = poDS->GetSession();
829     boolean bExists;
830     OCINumber *hNumber;
831 
832     OCICollGetElem( poSession->hEnv, poSession->hError,
833                     (OCIColl *)(hLastGeom->sdo_ordinates),
834                     (sb4)iOrdinal+0, (boolean *)&bExists,
835                     (dvoid **)&hNumber, nullptr );
836     OCINumberToReal(poSession->hError, hNumber,
837                     (uword)sizeof(double), (dvoid *)pdfX);
838     OCICollGetElem( poSession->hEnv, poSession->hError,
839                     (OCIColl *)(hLastGeom->sdo_ordinates),
840                     (sb4)iOrdinal + 1, (boolean *)&bExists,
841                     (dvoid **)&hNumber, nullptr );
842     OCINumberToReal(poSession->hError, hNumber,
843                     (uword)sizeof(double), (dvoid *)pdfY);
844     if( nDimension == 3 )
845     {
846         OCICollGetElem( poSession->hEnv, poSession->hError,
847                         (OCIColl *)(hLastGeom->sdo_ordinates),
848                         (sb4)iOrdinal + 2, (boolean *)&bExists,
849                         (dvoid **)&hNumber, nullptr );
850         OCINumberToReal(poSession->hError, hNumber,
851                         (uword)sizeof(double), (dvoid *)pdfZ);
852     }
853 
854     return TRUE;
855 }
856 
857 /************************************************************************/
858 /*                           TestCapability()                           */
859 /************************************************************************/
860 
TestCapability(const char * pszCap)861 int OGROCILayer::TestCapability( const char * pszCap )
862 
863 {
864     if( EQUAL(pszCap,OLCRandomRead) )
865         return TRUE;
866 
867     else if( EQUAL(pszCap,OLCFastFeatureCount) )
868         return m_poFilterGeom == nullptr;
869 
870     else if( EQUAL(pszCap,OLCFastSpatialFilter) )
871         return TRUE;
872 
873     else if( EQUAL(pszCap,OLCTransactions) )
874         return TRUE;
875 
876     else
877         return FALSE;
878 }
879 
880 /************************************************************************/
881 /*                          LookupTableSRID()                           */
882 /*                                                                      */
883 /*      Note that the table name may also be prefixed by the owner      */
884 /*      with a dot separator.                                           */
885 /************************************************************************/
886 
LookupTableSRID()887 int OGROCILayer::LookupTableSRID()
888 
889 {
890 /* -------------------------------------------------------------------- */
891 /*      If we don't have a geometry column, there isn't much point      */
892 /*      in trying.                                                      */
893 /* -------------------------------------------------------------------- */
894     if( pszGeomName == nullptr )
895         return -1;
896 
897 /* -------------------------------------------------------------------- */
898 /*      Split out the owner if available.                               */
899 /* -------------------------------------------------------------------- */
900     const char *pszTableName = GetLayerDefn()->GetName();
901     char *pszOwner = nullptr;
902 
903     if( strstr(pszTableName,".") != nullptr )
904     {
905         pszOwner = CPLStrdup(pszTableName);
906         pszTableName = strstr(pszTableName,".") + 1;
907 
908         *(strstr(pszOwner,".")) = '\0';
909     }
910 
911 /* -------------------------------------------------------------------- */
912 /*      Build our query command.                                        */
913 /* -------------------------------------------------------------------- */
914     OGROCIStringBuf oCommand;
915 
916     oCommand.Append( "SELECT SRID FROM ALL_SDO_GEOM_METADATA "
917                       "WHERE TABLE_NAME = UPPER(:table_name) AND COLUMN_NAME = UPPER(:geometry_name)" );
918 
919     if( pszOwner != nullptr )
920     {
921         oCommand.Append( " AND OWNER = :owner");
922     }
923 
924 /* -------------------------------------------------------------------- */
925 /*      Execute query command.                                          */
926 /* -------------------------------------------------------------------- */
927     OGROCIStatement oGetTables( poDS->GetSession() );
928     int nSRID = -1;
929 
930     if( oGetTables.Prepare( oCommand.GetString() ) != CE_None )
931         return nSRID;
932 
933     oGetTables.BindString(":table_name", pszTableName);
934     oGetTables.BindString(":geometry_name", pszGeomName);
935     if( pszOwner != nullptr )
936     {
937         oGetTables.BindString(":owner", pszOwner);
938         CPLFree( pszOwner );
939     }
940 
941     if( oGetTables.Execute( nullptr ) == CE_None )
942     {
943         char **papszRow = oGetTables.SimpleFetchRow();
944 
945         if( papszRow != nullptr && papszRow[0] != nullptr )
946             nSRID = atoi( papszRow[0] );
947     }
948 
949     return nSRID;
950 }
951 
952 /************************************************************************/
953 /*                            GetFIDColumn()                            */
954 /************************************************************************/
955 
GetFIDColumn()956 const char *OGROCILayer::GetFIDColumn()
957 
958 {
959     if( pszFIDName != nullptr )
960         return pszFIDName;
961     else
962         return "";
963 }
964 
965 /************************************************************************/
966 /*                         GetGeometryColumn()                          */
967 /************************************************************************/
968 
GetGeometryColumn()969 const char *OGROCILayer::GetGeometryColumn()
970 
971 {
972     if( pszGeomName != nullptr )
973         return pszGeomName;
974     else
975         return "";
976 }
977