1 /******************************************************************************
2  * $Id: ogrociwritablelayer.cpp 28481 2015-02-13 17:11:15Z rouault $
3  *
4  * Project:  Oracle Spatial Driver
5  * Purpose:  Implementation of the OGROCIWritableLayer class.  This provides
6  *           some services for converting OGRGeometries into Oracle structures
7  *           that is shared between OGROCITableLayer and OGROCILoaderLayer.
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: ogrociwritablelayer.cpp 28481 2015-02-13 17:11:15Z rouault $");
37 
38 /************************************************************************/
39 /*                        OGROCIWritableLayer()                         */
40 /************************************************************************/
41 
OGROCIWritableLayer()42 OGROCIWritableLayer::OGROCIWritableLayer()
43 
44 {
45     nDimension = MAX(2,MIN(3,atoi(CPLGetConfigOption("OCI_DEFAULT_DIM","3"))));
46     nSRID = -1;
47 
48     nOrdinalCount = 0;
49     nOrdinalMax = 0;
50     padfOrdinals = NULL;
51 
52     nElemInfoCount = 0;
53     nElemInfoMax = 0;
54     panElemInfo = NULL;
55 
56     bLaunderColumnNames = TRUE;
57     bTruncationReported = FALSE;
58     nSRID = -1;
59     poSRS = NULL;
60 
61     papszOptions = NULL;
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 = (OGRPoint *) poGeometry;
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 = (OGRLineString *) poGeometry;
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 = (OGRPolygon *) poGeometry;
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[30];     // specify at most 30 characters, see ORA-00972
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             sprintf( 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             sprintf( 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             sprintf( 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             strcpy( szFieldType, "VARCHAR2(2047)" );
295         else
296             sprintf( szFieldType, "VARCHAR2(%d)", oField.GetWidth() );
297     }
298     else if ( oField.GetType() == OFTDate )
299     {
300         sprintf( szFieldType, "DATE" );
301     }
302     else if ( oField.GetType() == OFTDateTime )
303     {
304         sprintf( szFieldType, "TIMESTAMP" );
305     }
306     else if( bApproxOK )
307     {
308         oField.SetDefault(NULL);
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         strcpy( szFieldType, "VARCHAR2(2047)" );
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     oCommand.MakeRoomFor( 70 + strlen(poFeatureDefn->GetName())
332                           + strlen(oField.GetNameRef())
333                           + strlen(szFieldType)
334                           + (oField.GetDefault() ? strlen(oField.GetDefault()) : 0) );
335 
336     snprintf( szFieldName, sizeof( szFieldName ), "%s", oField.GetNameRef());
337     szFieldName[sizeof( szFieldName )-1] = '\0';
338     if ( strlen(oField.GetNameRef()) > sizeof ( szFieldName ) )
339     {
340         szFieldName[sizeof( szFieldName ) - 1] = '_';
341         CPLError( CE_Warning, CPLE_AppDefined,
342                   "Column %s is too long (at most 30 characters). Using %s.",
343                   oField.GetNameRef(), szFieldName );
344         oField.SetName(szFieldName);
345     }
346     sprintf( oCommand.GetString(), "ALTER TABLE %s ADD \"%s\" %s",
347              poFeatureDefn->GetName(), szFieldName, szFieldType);
348     if( oField.GetDefault() != NULL && !oField.IsDefaultDriverSpecific() )
349     {
350         sprintf( oCommand.GetString() + strlen(oCommand.GetString()),
351                  " DEFAULT %s", oField.GetDefault() );
352     }
353     if( !oField.IsNullable() )
354         strcat( oCommand.GetString(), " NOT NULL");
355 
356     if( oAddField.Execute( oCommand.GetString() ) != CE_None )
357         return OGRERR_FAILURE;
358 
359     poFeatureDefn->AddFieldDefn( &oField );
360 
361     return OGRERR_NONE;
362 }
363 
364 /************************************************************************/
365 /*                            SetDimension()                            */
366 /************************************************************************/
367 
SetDimension(int nNewDim)368 void OGROCIWritableLayer::SetDimension( int nNewDim )
369 
370 {
371     nDimension = nNewDim;
372 }
373 
374 /************************************************************************/
375 /*                            ParseDIMINFO()                            */
376 /************************************************************************/
377 
ParseDIMINFO(const char * pszOptionName,double * pdfMin,double * pdfMax,double * pdfRes)378 void OGROCIWritableLayer::ParseDIMINFO( const char *pszOptionName,
379                                         double *pdfMin,
380                                         double *pdfMax,
381                                         double *pdfRes )
382 
383 {
384     const char *pszUserDIMINFO;
385     char **papszTokens;
386 
387     pszUserDIMINFO = CSLFetchNameValue( papszOptions, pszOptionName );
388     if( pszUserDIMINFO == NULL )
389         return;
390 
391     papszTokens =
392         CSLTokenizeStringComplex( pszUserDIMINFO, ",", FALSE, FALSE );
393     if( CSLCount(papszTokens) != 3 )
394     {
395         CSLDestroy( papszTokens );
396         CPLError( CE_Warning, CPLE_AppDefined,
397                   "Ignoring %s, it does not contain three comma separated values.",
398                   pszOptionName );
399         return;
400     }
401 
402     *pdfMin = CPLAtof(papszTokens[0]);
403     *pdfMax = CPLAtof(papszTokens[1]);
404     *pdfRes = CPLAtof(papszTokens[2]);
405 
406     CSLDestroy( papszTokens );
407 }
408 
409 /************************************************************************/
410 /*                       TranslateToSDOGeometry()                       */
411 /************************************************************************/
412 
TranslateToSDOGeometry(OGRGeometry * poGeometry,int * pnGType)413 OGRErr OGROCIWritableLayer::TranslateToSDOGeometry( OGRGeometry * poGeometry,
414                                                     int *pnGType )
415 
416 {
417     nOrdinalCount = 0;
418     nElemInfoCount = 0;
419 
420     if( poGeometry == NULL )
421         return OGRERR_FAILURE;
422 
423 /* ==================================================================== */
424 /*      Handle a point geometry.                                        */
425 /* ==================================================================== */
426     if( wkbFlatten(poGeometry->getGeometryType()) == wkbPoint )
427     {
428 #ifdef notdef
429         char szResult[1024];
430         OGRPoint *poPoint = (OGRPoint *) poGeometry;
431 
432         if( nDimension == 2 )
433             CPLsprintf( szResult,
434                      "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,0.0),NULL,NULL)",
435                      SDO_GEOMETRY, 2001, szSRID,
436                      poPoint->getX(), poPoint->getY() );
437         else
438             CPLsprintf( szResult,
439                      "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,%.16g),NULL,NULL)",
440                      SDO_GEOMETRY, 3001, szSRID,
441                      poPoint->getX(), poPoint->getY(), poPoint->getZ() );
442 
443         return CPLStrdup(szResult );
444 #endif
445     }
446 
447 /* ==================================================================== */
448 /*      Handle a line string geometry.                                  */
449 /* ==================================================================== */
450     else if( wkbFlatten(poGeometry->getGeometryType()) == wkbLineString )
451     {
452         *pnGType = nDimension * 1000 + 2;
453         TranslateElementGroup( poGeometry );
454         return OGRERR_NONE;
455     }
456 
457 /* ==================================================================== */
458 /*      Handle a polygon geometry.                                      */
459 /* ==================================================================== */
460     else if( wkbFlatten(poGeometry->getGeometryType()) == wkbPolygon )
461     {
462         *pnGType = nDimension == 2 ? 2003 : 3003;
463         TranslateElementGroup( poGeometry );
464         return OGRERR_NONE;
465     }
466 
467 /* ==================================================================== */
468 /*      Handle a multi point geometry.                                  */
469 /* ==================================================================== */
470     else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint )
471     {
472         OGRMultiPoint *poMP = (OGRMultiPoint *) poGeometry;
473         int  iVert;
474 
475         *pnGType = nDimension*1000 + 5;
476         PushElemInfo( 1, 1, poMP->getNumGeometries() );
477 
478         for( iVert = 0; iVert < poMP->getNumGeometries(); iVert++ )
479         {
480             OGRPoint *poPoint = (OGRPoint *)poMP->getGeometryRef( iVert );
481 
482             PushOrdinal( poPoint->getX() );
483             PushOrdinal( poPoint->getY() );
484             if( nDimension == 3 )
485                 PushOrdinal( poPoint->getZ() );
486         }
487 
488         return OGRERR_NONE;
489     }
490 
491 /* ==================================================================== */
492 /*      Handle other geometry collections.                              */
493 /* ==================================================================== */
494     else
495     {
496 /* -------------------------------------------------------------------- */
497 /*      Identify the GType.                                             */
498 /* -------------------------------------------------------------------- */
499         if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString )
500             *pnGType = nDimension * 1000 + 6;
501         else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon )
502             *pnGType = nDimension * 1000 + 7;
503         else if( wkbFlatten(poGeometry->getGeometryType())
504                  == wkbGeometryCollection )
505             *pnGType = nDimension * 1000 + 4;
506         else
507         {
508             CPLError( CE_Failure, CPLE_AppDefined,
509                       "Unexpected geometry type (%d/%s) in "
510                       "OGROCIWritableLayer::TranslateToSDOGeometry()",
511                       poGeometry->getGeometryType(),
512                       poGeometry->getGeometryName() );
513             return OGRERR_FAILURE;
514         }
515 
516 /* -------------------------------------------------------------------- */
517 /*      Translate each child in turn.                                   */
518 /* -------------------------------------------------------------------- */
519         OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeometry;
520         int  iChild;
521 
522         for( iChild = 0; iChild < poGC->getNumGeometries(); iChild++ )
523             TranslateElementGroup( poGC->getGeometryRef(iChild) );
524 
525         return OGRERR_NONE;
526     }
527 
528     return OGRERR_FAILURE;
529 }
530 
FindFieldIndex(const char * pszFieldName,int bExactMatch)531 int OGROCIWritableLayer::FindFieldIndex( const char *pszFieldName, int bExactMatch )
532 {
533   int iField = GetLayerDefn()->GetFieldIndex( pszFieldName );
534 
535   if( !bExactMatch && iField < 0 )
536   {
537       // try laundered version
538       OGROCISession *poSession = poDS->GetSession();
539       char *pszSafeName = CPLStrdup( pszFieldName );
540 
541       poSession->CleanName( pszSafeName );
542 
543       iField = GetLayerDefn()->GetFieldIndex( pszSafeName );
544 
545       CPLFree( pszSafeName );
546   }
547 
548   return iField;
549 }
550 
551