1 /******************************************************************************
2  *
3  * Project:  Oracle Spatial Driver
4  * Purpose:  Implementation of the OGROCIWritableLayer class.  This provides
5  *           some services for converting OGRGeometries into Oracle structures
6  *           that is shared between OGROCITableLayer and OGROCILoaderLayer.
7  * Author:   Frank Warmerdam, warmerdam@pobox.com
8  *
9  ******************************************************************************
10  * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "ogr_oci.h"
32 #include "cpl_conv.h"
33 #include "cpl_string.h"
34 
35 CPL_CVSID("$Id: ogrociwritablelayer.cpp 3299482632a616871b0427f192f706caf5669e81 2018-04-01 01:20:00 +0200 Even Rouault $")
36 
37 /************************************************************************/
38 /*                        OGROCIWritableLayer()                         */
39 /************************************************************************/
40 
OGROCIWritableLayer()41 OGROCIWritableLayer::OGROCIWritableLayer()
42 
43 {
44     nDimension = MAX(2,MIN(3,atoi(CPLGetConfigOption("OCI_DEFAULT_DIM","3"))));
45     nSRID = -1;
46 
47     nOrdinalCount = 0;
48     nOrdinalMax = 0;
49     padfOrdinals = nullptr;
50 
51     nElemInfoCount = 0;
52     nElemInfoMax = 0;
53     panElemInfo = nullptr;
54 
55     bLaunderColumnNames = TRUE;
56     bPreservePrecision = FALSE;
57     nDefaultStringSize = DEFAULT_STRING_SIZE;
58     bTruncationReported = FALSE;
59     poSRS = nullptr;
60 
61     papszOptions = nullptr;
62 }
63 
64 /************************************************************************/
65 /*                        ~OGROCIWritableLayer()                        */
66 /************************************************************************/
67 
~OGROCIWritableLayer()68 OGROCIWritableLayer::~OGROCIWritableLayer()
69 
70 {
71     CPLFree( padfOrdinals );
72     CPLFree( panElemInfo );
73 
74     CSLDestroy( papszOptions );
75 }
76 
77 /************************************************************************/
78 /*                            PushOrdinal()                             */
79 /************************************************************************/
80 
PushOrdinal(double dfOrd)81 void OGROCIWritableLayer::PushOrdinal( double dfOrd )
82 
83 {
84     if( nOrdinalCount == nOrdinalMax )
85     {
86         nOrdinalMax = nOrdinalMax * 2 + 100;
87         padfOrdinals = (double *) CPLRealloc(padfOrdinals,
88                                              sizeof(double) * nOrdinalMax);
89     }
90 
91     padfOrdinals[nOrdinalCount++] = dfOrd;
92 }
93 
94 /************************************************************************/
95 /*                            PushElemInfo()                            */
96 /************************************************************************/
97 
PushElemInfo(int nOffset,int nEType,int nInterp)98 void OGROCIWritableLayer::PushElemInfo( int nOffset, int nEType, int nInterp )
99 
100 {
101     if( nElemInfoCount+3 >= nElemInfoMax )
102     {
103         nElemInfoMax = nElemInfoMax * 2 + 100;
104         panElemInfo = (int *) CPLRealloc(panElemInfo,sizeof(int)*nElemInfoMax);
105     }
106 
107     panElemInfo[nElemInfoCount++] = nOffset;
108     panElemInfo[nElemInfoCount++] = nEType;
109     panElemInfo[nElemInfoCount++] = nInterp;
110 }
111 
112 /************************************************************************/
113 /*                       TranslateElementGroup()                        */
114 /*                                                                      */
115 /*      Append one or more element groups to the existing element       */
116 /*      info and ordinates lists for the passed geometry.               */
117 /************************************************************************/
118 
119 OGRErr
TranslateElementGroup(OGRGeometry * poGeometry)120 OGROCIWritableLayer::TranslateElementGroup( OGRGeometry *poGeometry )
121 
122 {
123     switch( wkbFlatten(poGeometry->getGeometryType()) )
124     {
125       case wkbPoint:
126       {
127           OGRPoint *poPoint = poGeometry->toPoint();
128 
129           PushElemInfo( nOrdinalCount+1, 1, 1 );
130 
131           PushOrdinal( poPoint->getX() );
132           PushOrdinal( poPoint->getY() );
133           if( nDimension == 3 )
134               PushOrdinal( poPoint->getZ() );
135 
136           return OGRERR_NONE;
137       }
138 
139       case wkbLineString:
140       {
141           OGRLineString *poLine = poGeometry->toLineString();
142           int  iVert;
143 
144           PushElemInfo( nOrdinalCount+1, 2, 1 );
145 
146           for( iVert = 0; iVert < poLine->getNumPoints(); iVert++ )
147           {
148               PushOrdinal( poLine->getX(iVert) );
149               PushOrdinal( poLine->getY(iVert) );
150               if( nDimension == 3 )
151                   PushOrdinal( poLine->getZ(iVert) );
152           }
153           return OGRERR_NONE;
154       }
155 
156       case wkbPolygon:
157       {
158           OGRPolygon *poPoly = poGeometry->toPolygon();
159           int iRing;
160 
161           for( iRing = -1; iRing < poPoly->getNumInteriorRings(); iRing++ )
162           {
163               OGRLinearRing *poRing;
164               int            iVert;
165 
166               if( iRing == -1 )
167                   poRing = poPoly->getExteriorRing();
168               else
169                   poRing = poPoly->getInteriorRing(iRing);
170 
171               if( iRing == -1 )
172                   PushElemInfo( nOrdinalCount+1, 1003, 1 );
173               else
174                   PushElemInfo( nOrdinalCount+1, 2003, 1 );
175 
176               if( (iRing == -1 && poRing->isClockwise())
177                   || (iRing != -1 && !poRing->isClockwise()) )
178               {
179                   for( iVert = poRing->getNumPoints()-1; iVert >= 0; iVert-- )
180                   {
181                       PushOrdinal( poRing->getX(iVert) );
182                       PushOrdinal( poRing->getY(iVert) );
183                       if( nDimension == 3 )
184                           PushOrdinal( poRing->getZ(iVert) );
185                   }
186               }
187               else
188               {
189                   for( iVert = 0; iVert < poRing->getNumPoints(); iVert++ )
190                   {
191                       PushOrdinal( poRing->getX(iVert) );
192                       PushOrdinal( poRing->getY(iVert) );
193                       if( nDimension == 3 )
194                           PushOrdinal( poRing->getZ(iVert) );
195                   }
196               }
197           }
198 
199           return OGRERR_NONE;
200       }
201 
202       default:
203       {
204           return OGRERR_FAILURE;
205       }
206     }
207 }
208 
209 /************************************************************************/
210 /*                          ReportTruncation()                          */
211 /************************************************************************/
212 
ReportTruncation(OGRFieldDefn * psFldDefn)213 void OGROCIWritableLayer::ReportTruncation( OGRFieldDefn * psFldDefn )
214 
215 {
216     if( bTruncationReported )
217         return;
218 
219     CPLError( CE_Warning, CPLE_AppDefined,
220               "The value for the field %s is being truncated to fit the\n"
221               "declared width/precision of the field.  No more truncations\n"
222               "for table %s will be reported.",
223               psFldDefn->GetNameRef(), poFeatureDefn->GetName() );
224 
225     bTruncationReported = TRUE;
226 }
227 
228 /************************************************************************/
229 /*                             SetOptions()                             */
230 /*                                                                      */
231 /*      Set layer creation or other options.                            */
232 /************************************************************************/
233 
SetOptions(char ** papszOptionsIn)234 void OGROCIWritableLayer::SetOptions( char **papszOptionsIn )
235 
236 {
237     CSLDestroy( papszOptions );
238     papszOptions = CSLDuplicate( papszOptionsIn );
239 }
240 
241 /************************************************************************/
242 /*                            CreateField()                             */
243 /************************************************************************/
244 
CreateField(OGRFieldDefn * poFieldIn,int bApproxOK)245 OGRErr OGROCIWritableLayer::CreateField( OGRFieldDefn *poFieldIn, int bApproxOK )
246 
247 {
248     OGROCISession      *poSession = poDS->GetSession();
249     char                szFieldType[256];
250     char                szFieldName[128]; // 12.2 max identifier name
251     OGRFieldDefn        oField( poFieldIn );
252 
253 /* -------------------------------------------------------------------- */
254 /*      Do we want to "launder" the column names into Oracle            */
255 /*      friendly format?                                                */
256 /* -------------------------------------------------------------------- */
257     if( bLaunderColumnNames )
258     {
259         char    *pszSafeName = CPLStrdup( oField.GetNameRef() );
260 
261         poSession->CleanName( pszSafeName );
262         oField.SetName( pszSafeName );
263         CPLFree( pszSafeName );
264     }
265 
266 /* -------------------------------------------------------------------- */
267 /*      Work out the Oracle type.                                       */
268 /* -------------------------------------------------------------------- */
269     if( oField.GetType() == OFTInteger )
270     {
271         if( bPreservePrecision && oField.GetWidth() != 0 )
272             snprintf( szFieldType, sizeof(szFieldType), "NUMBER(%d)", oField.GetWidth() );
273         else
274             strcpy( szFieldType, "INTEGER" );
275     }
276     else if( oField.GetType() == OFTInteger64 )
277     {
278         if( bPreservePrecision && oField.GetWidth() != 0 )
279             snprintf( szFieldType, sizeof(szFieldType), "NUMBER(%d)", oField.GetWidth() );
280         else
281             strcpy( szFieldType, "NUMBER(20)" );
282     }
283     else if( oField.GetType() == OFTReal )
284     {
285         if( bPreservePrecision && oField.GetWidth() != 0 )
286             snprintf( szFieldType, sizeof(szFieldType), "NUMBER(%d,%d)",
287                      oField.GetWidth(), oField.GetPrecision() );
288         else
289             strcpy( szFieldType, "FLOAT(126)" );
290     }
291     else if( oField.GetType() == OFTString )
292     {
293         if( oField.GetWidth() == 0 || !bPreservePrecision )
294             snprintf( szFieldType, sizeof(szFieldType), "VARCHAR2(%d)", nDefaultStringSize );
295         else
296             snprintf( szFieldType, sizeof(szFieldType), "VARCHAR2(%d)", oField.GetWidth() );
297     }
298     else if ( oField.GetType() == OFTDate )
299     {
300         snprintf( szFieldType, sizeof(szFieldType), "DATE" );
301     }
302     else if ( oField.GetType() == OFTDateTime )
303     {
304         snprintf( szFieldType, sizeof(szFieldType), "TIMESTAMP" );
305     }
306     else if( bApproxOK )
307     {
308         oField.SetDefault(nullptr);
309         CPLError( CE_Warning, CPLE_NotSupported,
310                   "Can't create field %s with type %s on Oracle layers.  Creating as VARCHAR.",
311                   oField.GetNameRef(),
312                   OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
313         snprintf( szFieldType, sizeof(szFieldType), "VARCHAR2(%d)", nDefaultStringSize );
314     }
315     else
316     {
317         CPLError( CE_Failure, CPLE_NotSupported,
318                   "Can't create field %s with type %s on Oracle layers.",
319                   oField.GetNameRef(),
320                   OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
321 
322         return OGRERR_FAILURE;
323     }
324 
325 /* -------------------------------------------------------------------- */
326 /*      Create the new field.                                           */
327 /* -------------------------------------------------------------------- */
328     OGROCIStringBuf     oCommand;
329     OGROCIStatement     oAddField( poSession );
330 
331     const int nCommandSize = static_cast<int>(70 + strlen(poFeatureDefn->GetName())
332                           + strlen(oField.GetNameRef())
333                           + strlen(szFieldType)
334                           + (oField.GetDefault() ? strlen(oField.GetDefault()) : 0));
335     oCommand.MakeRoomFor( nCommandSize );
336 
337     snprintf( szFieldName, sizeof( szFieldName ), "%s", oField.GetNameRef());
338     szFieldName[sizeof( szFieldName )-1] = '\0';
339     if ( strlen(oField.GetNameRef()) > sizeof ( szFieldName ) )
340     {
341         szFieldName[sizeof( szFieldName ) - 1] = '_';
342         CPLError( CE_Warning, CPLE_AppDefined,
343                   "Column %s is too long (at most 30 characters). Using %s.",
344                   oField.GetNameRef(), szFieldName );
345         oField.SetName(szFieldName);
346     }
347     snprintf( oCommand.GetString(), nCommandSize, "ALTER TABLE %s ADD \"%s\" %s",
348              poFeatureDefn->GetName(), szFieldName, szFieldType);
349     if( oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific() )
350     {
351         snprintf( oCommand.GetString() + strlen(oCommand.GetString()),
352                   nCommandSize - strlen(oCommand.GetString()),
353                  " DEFAULT %s", oField.GetDefault() );
354     }
355     if( !oField.IsNullable() )
356         strcat( oCommand.GetString(), " NOT NULL");
357 
358     if( oAddField.Execute( oCommand.GetString() ) != CE_None )
359         return OGRERR_FAILURE;
360 
361     poFeatureDefn->AddFieldDefn( &oField );
362 
363     return OGRERR_NONE;
364 }
365 
366 /************************************************************************/
367 /*                            SetDimension()                            */
368 /************************************************************************/
369 
SetDimension(int nNewDim)370 void OGROCIWritableLayer::SetDimension( int nNewDim )
371 
372 {
373     nDimension = nNewDim;
374 }
375 
376 /************************************************************************/
377 /*                            ParseDIMINFO()                            */
378 /************************************************************************/
379 
ParseDIMINFO(const char * pszOptionName,double * pdfMin,double * pdfMax,double * pdfRes)380 void OGROCIWritableLayer::ParseDIMINFO( const char *pszOptionName,
381                                         double *pdfMin,
382                                         double *pdfMax,
383                                         double *pdfRes )
384 
385 {
386     const char *pszUserDIMINFO;
387     char **papszTokens;
388 
389     pszUserDIMINFO = CSLFetchNameValue( papszOptions, pszOptionName );
390     if( pszUserDIMINFO == nullptr )
391         return;
392 
393     papszTokens =
394         CSLTokenizeStringComplex( pszUserDIMINFO, ",", FALSE, FALSE );
395     if( CSLCount(papszTokens) != 3 )
396     {
397         CSLDestroy( papszTokens );
398         CPLError( CE_Warning, CPLE_AppDefined,
399                   "Ignoring %s, it does not contain three comma separated values.",
400                   pszOptionName );
401         return;
402     }
403 
404     *pdfMin = CPLAtof(papszTokens[0]);
405     *pdfMax = CPLAtof(papszTokens[1]);
406     *pdfRes = CPLAtof(papszTokens[2]);
407 
408     CSLDestroy( papszTokens );
409 }
410 
411 /************************************************************************/
412 /*                       TranslateToSDOGeometry()                       */
413 /************************************************************************/
414 
TranslateToSDOGeometry(OGRGeometry * poGeometry,int * pnGType)415 OGRErr OGROCIWritableLayer::TranslateToSDOGeometry( OGRGeometry * poGeometry,
416                                                     int *pnGType )
417 
418 {
419     nOrdinalCount = 0;
420     nElemInfoCount = 0;
421 
422     if( poGeometry == nullptr )
423         return OGRERR_FAILURE;
424 
425 /* ==================================================================== */
426 /*      Handle a point geometry.                                        */
427 /* ==================================================================== */
428     if( wkbFlatten(poGeometry->getGeometryType()) == wkbPoint )
429     {
430 #ifdef notdef
431         char szResult[1024];
432         OGRPoint *poPoint = poGeometry->toPoint();
433 
434         if( nDimension == 2 )
435             CPLsprintf( szResult,
436                      "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,0.0),NULL,NULL)",
437                      SDO_GEOMETRY, 2001, szSRID,
438                      poPoint->getX(), poPoint->getY() );
439         else
440             CPLsprintf( szResult,
441                      "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,%.16g),NULL,NULL)",
442                      SDO_GEOMETRY, 3001, szSRID,
443                      poPoint->getX(), poPoint->getY(), poPoint->getZ() );
444 
445         return CPLStrdup(szResult );
446 #endif
447     }
448 
449 /* ==================================================================== */
450 /*      Handle a line string geometry.                                  */
451 /* ==================================================================== */
452     else if( wkbFlatten(poGeometry->getGeometryType()) == wkbLineString )
453     {
454         *pnGType = nDimension * 1000 + 2;
455         TranslateElementGroup( poGeometry );
456         return OGRERR_NONE;
457     }
458 
459 /* ==================================================================== */
460 /*      Handle a polygon geometry.                                      */
461 /* ==================================================================== */
462     else if( wkbFlatten(poGeometry->getGeometryType()) == wkbPolygon )
463     {
464         *pnGType = nDimension == 2 ? 2003 : 3003;
465         TranslateElementGroup( poGeometry );
466         return OGRERR_NONE;
467     }
468 
469 /* ==================================================================== */
470 /*      Handle a multi point geometry.                                  */
471 /* ==================================================================== */
472     else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint )
473     {
474         OGRMultiPoint *poMP = poGeometry->toMultiPoint();
475 
476         *pnGType = nDimension*1000 + 5;
477         PushElemInfo( 1, 1, poMP->getNumGeometries() );
478 
479         for( auto&& poPoint: *poMP )
480         {
481             PushOrdinal( poPoint->getX() );
482             PushOrdinal( poPoint->getY() );
483             if( nDimension == 3 )
484                 PushOrdinal( poPoint->getZ() );
485         }
486 
487         return OGRERR_NONE;
488     }
489 
490 /* ==================================================================== */
491 /*      Handle other geometry collections.                              */
492 /* ==================================================================== */
493     else
494     {
495 /* -------------------------------------------------------------------- */
496 /*      Identify the GType.                                             */
497 /* -------------------------------------------------------------------- */
498         if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString )
499             *pnGType = nDimension * 1000 + 6;
500         else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon )
501             *pnGType = nDimension * 1000 + 7;
502         else if( wkbFlatten(poGeometry->getGeometryType())
503                  == wkbGeometryCollection )
504             *pnGType = nDimension * 1000 + 4;
505         else
506         {
507             CPLError( CE_Failure, CPLE_AppDefined,
508                       "Unexpected geometry type (%d/%s) in "
509                       "OGROCIWritableLayer::TranslateToSDOGeometry()",
510                       poGeometry->getGeometryType(),
511                       poGeometry->getGeometryName() );
512             return OGRERR_FAILURE;
513         }
514 
515 /* -------------------------------------------------------------------- */
516 /*      Translate each child in turn.                                   */
517 /* -------------------------------------------------------------------- */
518         OGRGeometryCollection *poGC = poGeometry->toGeometryCollection();
519         for( auto&& poMember: *poGC )
520             TranslateElementGroup( poMember );
521 
522         return OGRERR_NONE;
523     }
524 
525     return OGRERR_FAILURE;
526 }
527 
FindFieldIndex(const char * pszFieldName,int bExactMatch)528 int OGROCIWritableLayer::FindFieldIndex( const char *pszFieldName, int bExactMatch )
529 {
530   int iField = GetLayerDefn()->GetFieldIndex( pszFieldName );
531 
532   if( !bExactMatch && iField < 0 )
533   {
534       // try laundered version
535       OGROCISession *poSession = poDS->GetSession();
536       char *pszSafeName = CPLStrdup( pszFieldName );
537 
538       poSession->CleanName( pszSafeName );
539 
540       iField = GetLayerDefn()->GetFieldIndex( pszSafeName );
541 
542       CPLFree( pszSafeName );
543   }
544 
545   return iField;
546 }
547