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