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