1 /******************************************************************************
2 *
3 * Project: Oracle Spatial Driver
4 * Purpose: Implementation of the OGROCIDataSource class.
5 * Author: Frank Warmerdam, warmerdam@pobox.com
6 *
7 ******************************************************************************
8 * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 ****************************************************************************/
28
29 #include "ogr_oci.h"
30 #include "cpl_conv.h"
31 #include "cpl_string.h"
32
33 CPL_CVSID("$Id: ogrocidatasource.cpp 6c533b4f07cce657ec4a8b4d6163bf161ceafaa7 2021-06-24 15:38:31 +0200 Even Rouault $")
34
35 constexpr int anEPSGOracleMapping[] =
36 {
37 /* Oracle SRID, EPSG GCS/PCS Code */
38
39 8192, 4326, // WGS84
40 8306, 4322, // WGS72
41 8267, 4269, // NAD83
42 8274, 4277, // OSGB 36
43 // NAD27 isn't easily mapped since there are many Oracle NAD27 codes.
44
45 81989, 27700, // UK National Grid
46
47 0, 0 // end marker
48 };
49
50 /************************************************************************/
51 /* OGROCIDataSource() */
52 /************************************************************************/
53
OGROCIDataSource()54 OGROCIDataSource::OGROCIDataSource()
55
56 {
57 pszName = nullptr;
58 pszDBName = nullptr;
59 papoLayers = nullptr;
60 nLayers = 0;
61 bDSUpdate = FALSE;
62 bNoLogging = FALSE;
63 poSession = nullptr;
64 papoSRS = nullptr;
65 panSRID = nullptr;
66 nKnownSRID = 0;
67 }
68
69 /************************************************************************/
70 /* ~OGROCIDataSource() */
71 /************************************************************************/
72
~OGROCIDataSource()73 OGROCIDataSource::~OGROCIDataSource()
74
75 {
76 int i;
77
78 CPLFree( pszName );
79 CPLFree( pszDBName );
80
81 for( i = 0; i < nLayers; i++ )
82 delete papoLayers[i];
83
84 CPLFree( papoLayers );
85
86 for( i = 0; i < nKnownSRID; i++ )
87 {
88 papoSRS[i]->Release();
89 }
90 CPLFree( papoSRS );
91 CPLFree( panSRID );
92
93 if( poSession != nullptr )
94 delete poSession;
95 }
96
97 /************************************************************************/
98 /* Open() */
99 /************************************************************************/
100
Open(const char * pszNewName,char ** papszOpenOptionsIn,int bUpdate,int bTestOpen)101 int OGROCIDataSource::Open( const char * pszNewName,
102 char** papszOpenOptionsIn,
103 int bUpdate,
104 int bTestOpen )
105
106 {
107 CPLAssert( nLayers == 0 && poSession == nullptr );
108
109 /* -------------------------------------------------------------------- */
110 /* Verify Oracle prefix. */
111 /* -------------------------------------------------------------------- */
112 if( !STARTS_WITH_CI(pszNewName,"OCI:") )
113 {
114 if( !bTestOpen )
115 {
116 CPLError( CE_Failure, CPLE_AppDefined,
117 "%s does not conform to Oracle OCI driver naming convention,"
118 " OCI:*\n", pszNewName );
119 }
120 return FALSE;
121 }
122
123 /* -------------------------------------------------------------------- */
124 /* Try to parse out name, password and database name. */
125 /* -------------------------------------------------------------------- */
126 char *pszUserid;
127 const char *pszPassword = "";
128 const char *pszDatabase = "";
129 char **papszTableList = nullptr;
130 const char *pszWorkspace = "";
131
132 int i;
133
134 if( pszNewName[4] == '\0' )
135 {
136 pszUserid = CPLStrdup(CSLFetchNameValueDef(papszOpenOptionsIn, "USER", ""));
137 pszPassword = CSLFetchNameValueDef(papszOpenOptionsIn, "PASSWORD", "");
138 pszDatabase = CSLFetchNameValueDef(papszOpenOptionsIn, "DBNAME", "");
139 const char* pszTables = CSLFetchNameValue(papszOpenOptionsIn, "TABLES");
140 if( pszTables )
141 papszTableList = CSLTokenizeStringComplex(pszTables, ",", TRUE, FALSE );
142 pszWorkspace = CSLFetchNameValueDef(papszOpenOptionsIn, "WORKSPACE", "");
143 }
144 else
145 {
146 pszUserid = CPLStrdup( pszNewName + 4 );
147
148 // Is there a table list?
149 for( i = static_cast<int>(strlen(pszUserid))-1; i > 1; i-- )
150 {
151 if( pszUserid[i] == ':' )
152 {
153 papszTableList = CSLTokenizeStringComplex( pszUserid+i+1, ",",
154 TRUE, FALSE );
155 pszUserid[i] = '\0';
156 break;
157 }
158
159 if( pszUserid[i] == '/' || pszUserid[i] == '@' )
160 break;
161 }
162
163 for( i = 0;
164 pszUserid[i] != '\0' && pszUserid[i] != '/' && pszUserid[i] != '@';
165 i++ ) {}
166
167 if( pszUserid[i] == '/' )
168 {
169 pszUserid[i++] = '\0';
170 pszPassword = pszUserid + i;
171 for( ; pszUserid[i] != '\0' && pszUserid[i] != '@'; i++ ) {}
172 }
173
174 if( pszUserid[i] == '@' )
175 {
176 pszUserid[i++] = '\0';
177 pszDatabase = pszUserid + i;
178 }
179 }
180
181 /* -------------------------------------------------------------------- */
182 /* Try to establish connection. */
183 /* -------------------------------------------------------------------- */
184
185 poSession = OGRGetOCISession( pszUserid, pszPassword, pszDatabase );
186
187 if( poSession == nullptr )
188 {
189 CPLFree(pszUserid);
190 CSLDestroy(papszTableList);
191 return FALSE;
192 }
193
194 if( ! EQUAL(pszWorkspace, "") )
195 {
196 OGROCIStringBuf oValidateCmd;
197 OGROCIStatement oValidateStmt( GetSession() );
198
199 oValidateCmd.Append( "call DBMS_WM.GotoWorkspace('" );
200 oValidateCmd.Append( pszWorkspace );
201 oValidateCmd.Append( "')" );
202
203 oValidateStmt.Execute( oValidateCmd.GetString() );
204 }
205
206 pszName = CPLStrdup( pszNewName );
207
208 bDSUpdate = bUpdate;
209
210 /* -------------------------------------------------------------------- */
211 /* If no list of target tables was provided, collect a list of */
212 /* spatial tables now. */
213 /* -------------------------------------------------------------------- */
214 if( papszTableList == nullptr )
215 {
216 OGROCIStatement oGetTables( poSession );
217
218 if( oGetTables.Execute(
219 "SELECT TABLE_NAME, OWNER FROM ALL_SDO_GEOM_METADATA" )
220 == CE_None )
221 {
222 char **papszRow;
223
224 while( (papszRow = oGetTables.SimpleFetchRow()) != nullptr )
225 {
226 char szFullTableName[100];
227
228 if( EQUAL(papszRow[1],pszUserid) )
229 strcpy( szFullTableName, papszRow[0] );
230 else
231 snprintf( szFullTableName, sizeof(szFullTableName), "%s.%s",
232 papszRow[1], papszRow[0] );
233
234 if( CSLFindString( papszTableList, szFullTableName ) == -1 )
235 papszTableList = CSLAddString( papszTableList,
236 szFullTableName );
237 }
238 }
239 }
240 CPLFree( pszUserid );
241
242 /* -------------------------------------------------------------------- */
243 /* Open all the selected tables or views. */
244 /* -------------------------------------------------------------------- */
245 for( i = 0; papszTableList != nullptr && papszTableList[i] != nullptr; i++ )
246 {
247 OpenTable( papszTableList[i], -1, bUpdate, FALSE, papszOpenOptionsIn );
248 }
249
250 CSLDestroy( papszTableList );
251
252 return TRUE;
253 }
254
255 /************************************************************************/
256 /* OpenTable() */
257 /************************************************************************/
258
OpenTable(const char * pszNewName,int nSRID,int bUpdate,CPL_UNUSED int bTestOpen,char ** papszOpenOptionsIn)259 int OGROCIDataSource::OpenTable( const char *pszNewName,
260 int nSRID, int bUpdate, CPL_UNUSED int bTestOpen,
261 char** papszOpenOptionsIn )
262
263 {
264 /* -------------------------------------------------------------------- */
265 /* Create the layer object. */
266 /* -------------------------------------------------------------------- */
267 OGROCITableLayer *poLayer;
268
269 poLayer = new OGROCITableLayer( this, pszNewName, wkbUnknown, nSRID,
270 bUpdate, FALSE );
271
272 if( !poLayer->IsValid() )
273 {
274 delete poLayer;
275 return FALSE;
276 }
277
278 /* -------------------------------------------------------------------- */
279 /* Add layer to data source layer list. */
280 /* -------------------------------------------------------------------- */
281 papoLayers = (OGROCILayer **)
282 CPLRealloc( papoLayers, sizeof(OGROCILayer *) * (nLayers+1) );
283 papoLayers[nLayers++] = poLayer;
284
285 poLayer->SetOptions( papszOpenOptionsIn );
286
287 return TRUE;
288 }
289
290 /************************************************************************/
291 /* ValidateLayer() */
292 /************************************************************************/
293
ValidateLayer(const char * pszLayerName)294 void OGROCIDataSource::ValidateLayer( const char *pszLayerName )
295
296 {
297 int iLayer;
298
299 /* -------------------------------------------------------------------- */
300 /* Try to find layer. */
301 /* -------------------------------------------------------------------- */
302 for( iLayer = 0; iLayer < nLayers; iLayer++ )
303 {
304 if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) )
305 break;
306 }
307
308 if( iLayer == nLayers )
309 {
310 CPLError( CE_Failure, CPLE_AppDefined,
311 "ValidateLayer(): %s is not a recognised layer.",
312 pszLayerName );
313 return;
314 }
315
316 /* -------------------------------------------------------------------- */
317 /* Verify we have an FID and geometry column for this table. */
318 /* -------------------------------------------------------------------- */
319 OGROCITableLayer *poLayer = (OGROCITableLayer *) papoLayers[iLayer];
320
321 if( strlen(poLayer->GetFIDColumn()) == 0 )
322 {
323 CPLError( CE_Failure, CPLE_AppDefined,
324 "ValidateLayer(): %s lacks a fid column.",
325 pszLayerName );
326
327 return;
328 }
329
330 /* -------------------------------------------------------------------- */
331 /* Prepare and execute the geometry validation. */
332 /* -------------------------------------------------------------------- */
333
334 if( strlen(poLayer->GetGeometryColumn()) != 0 )
335 {
336 OGROCIStringBuf oValidateCmd;
337 OGROCIStatement oValidateStmt( GetSession() );
338
339 oValidateCmd.Append( "SELECT c." );
340 oValidateCmd.Append( poLayer->GetFIDColumn() );
341 oValidateCmd.Append( ", SDO_GEOM.VALIDATE_GEOMETRY(c." );
342 oValidateCmd.Append( poLayer->GetGeometryColumn() );
343 oValidateCmd.Append( ", m.diminfo) from " );
344 oValidateCmd.Append( poLayer->GetLayerDefn()->GetName() );
345 oValidateCmd.Append( " c, user_sdo_geom_metadata m WHERE m.table_name= '");
346 oValidateCmd.Append( poLayer->GetLayerDefn()->GetName() );
347 oValidateCmd.Append( "' AND m.column_name = '" );
348 oValidateCmd.Append( poLayer->GetGeometryColumn() );
349 oValidateCmd.Append( "' AND SDO_GEOM.VALIDATE_GEOMETRY(c." );
350 oValidateCmd.Append( poLayer->GetGeometryColumn() );
351 oValidateCmd.Append( ", m.diminfo ) <> 'TRUE'" );
352
353 oValidateStmt.Execute( oValidateCmd.GetString() );
354
355 /* -------------------------------------------------------------------- */
356 /* Report results to debug stream. */
357 /* -------------------------------------------------------------------- */
358 char **papszRow;
359
360 while( (papszRow = oValidateStmt.SimpleFetchRow()) != nullptr )
361 {
362 const char *pszReason = papszRow[1];
363
364 if( EQUAL(pszReason,"13011") )
365 pszReason = "13011: value is out of range";
366 else if( EQUAL(pszReason,"13050") )
367 pszReason = "13050: unable to construct spatial object";
368 else if( EQUAL(pszReason,"13349") )
369 pszReason = "13349: polygon boundary crosses itself";
370
371 CPLDebug( "OCI", "Validation failure for FID=%s: %s",
372 papszRow[0], pszReason );
373 }
374 }
375 }
376
377 /************************************************************************/
378 /* DeleteLayer(int) */
379 /************************************************************************/
380
DeleteLayer(int iLayer)381 OGRErr OGROCIDataSource::DeleteLayer( int iLayer )
382
383 {
384 /* -------------------------------------------------------------------- */
385 /* Blow away our OGR structures related to the layer. This is */
386 /* pretty dangerous if anything has a reference to this layer! */
387 /* -------------------------------------------------------------------- */
388 CPLString osLayerName =
389 papoLayers[iLayer]->GetLayerDefn()->GetName();
390
391 CPLDebug( "OCI", "DeleteLayer(%s)", osLayerName.c_str() );
392
393 delete papoLayers[iLayer];
394 memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
395 sizeof(void *) * (nLayers - iLayer - 1) );
396 nLayers--;
397
398 /* -------------------------------------------------------------------- */
399 /* Remove from the database. */
400 /* -------------------------------------------------------------------- */
401 OGROCIStatement oCommand( poSession );
402 CPLString osCommand;
403 int nFailures = 0;
404
405 osCommand.Printf( "DROP TABLE \"%s\"", osLayerName.c_str() );
406 if( oCommand.Execute( osCommand ) != CE_None )
407 nFailures++;
408
409 osCommand.Printf(
410 "DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = UPPER('%s')",
411 osLayerName.c_str() );
412
413 if( oCommand.Execute( osCommand ) != CE_None )
414 nFailures++;
415
416 if( nFailures == 0 )
417 return OGRERR_NONE;
418 else
419 return OGRERR_FAILURE;
420 }
421
422 /************************************************************************/
423 /* DeleteLayer(const char *) */
424 /************************************************************************/
425
DeleteLayer(const char * pszLayerName)426 void OGROCIDataSource::DeleteLayer( const char *pszLayerName )
427
428 {
429 int iLayer;
430
431 /* -------------------------------------------------------------------- */
432 /* Try to find layer. */
433 /* -------------------------------------------------------------------- */
434 for( iLayer = 0; iLayer < nLayers; iLayer++ )
435 {
436 if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) )
437 {
438 break;
439 }
440 }
441
442 if( iLayer == nLayers )
443 {
444 CPLDebug( "OCI", "DeleteLayer: %s not found in layer list." \
445 " Layer *not* deleted.", pszLayerName );
446 return;
447 }
448
449 DeleteLayer( iLayer );
450 }
451
452 /************************************************************************/
453 /* TruncateLayer() */
454 /************************************************************************/
455
TruncateLayer(const char * pszLayerName)456 void OGROCIDataSource::TruncateLayer( const char *pszLayerName )
457
458 {
459
460 /* -------------------------------------------------------------------- */
461 /* Set OGR Debug statement explaining what is happening */
462 /* -------------------------------------------------------------------- */
463 CPLDebug( "OCI", "Truncate TABLE %s", pszLayerName );
464
465 /* -------------------------------------------------------------------- */
466 /* Truncate the layer in the database. */
467 /* -------------------------------------------------------------------- */
468 OGROCIStatement oCommand( poSession );
469 CPLString osCommand;
470
471 osCommand.Printf( "TRUNCATE TABLE \"%s\"", pszLayerName );
472 oCommand.Execute( osCommand );
473 }
474
475 /************************************************************************/
476 /* ICreateLayer() */
477 /************************************************************************/
478
479 OGRLayer *
ICreateLayer(const char * pszLayerName,OGRSpatialReference * poSRS,OGRwkbGeometryType eType,char ** papszOptions)480 OGROCIDataSource::ICreateLayer( const char * pszLayerName,
481 OGRSpatialReference *poSRS,
482 OGRwkbGeometryType eType,
483 char ** papszOptions )
484
485 {
486 char *pszSafeLayerName = CPLStrdup(pszLayerName);
487
488 poSession->CleanName( pszSafeLayerName );
489 CPLDebug( "OCI", "In Create Layer ..." );
490
491 bNoLogging = CPLFetchBool( papszOptions, "NO_LOGGING", false );
492
493 /* -------------------------------------------------------------------- */
494 /* Get the default string size */
495 /* -------------------------------------------------------------------- */
496
497 int nDefaultStringSize = DEFAULT_STRING_SIZE;
498
499 if (CSLFetchNameValue( papszOptions, "DEFAULT_STRING_SIZE" ) != nullptr)
500 {
501 nDefaultStringSize = atoi(
502 CSLFetchNameValue( papszOptions, "DEFAULT_STRING_SIZE" ) );
503 }
504
505 /* -------------------------------------------------------------------- */
506 /* Do we already have this layer? If so, should we blow it */
507 /* away? */
508 /* -------------------------------------------------------------------- */
509 int iLayer;
510 bool bIsNewLayer = TRUE;
511
512 if( CPLFetchBool( papszOptions, "TRUNCATE", false ) )
513 {
514 CPLDebug( "OCI", "Calling TruncateLayer for %s", pszLayerName );
515 TruncateLayer( pszSafeLayerName );
516 bIsNewLayer = false; // Oracle table already exists
517 }
518 else
519 {
520 for( iLayer = 0; iLayer < nLayers; iLayer++ )
521 {
522 if( EQUAL(pszSafeLayerName,
523 papoLayers[iLayer]->GetLayerDefn()->GetName()) )
524 {
525 if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != nullptr
526 && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
527 {
528 DeleteLayer( pszSafeLayerName );
529 }
530 else
531 {
532 CPLError( CE_Failure, CPLE_AppDefined,
533 "Layer %s already exists, CreateLayer failed.\n"
534 "Use the layer creation option OVERWRITE=YES to "
535 "replace it.",
536 pszSafeLayerName );
537 CPLFree( pszSafeLayerName );
538 return nullptr;
539 }
540 }
541 }
542 }
543
544 /* -------------------------------------------------------------------- */
545 /* Try to get the SRS Id of this spatial reference system, */
546 /* adding tot the srs table if needed. */
547 /* -------------------------------------------------------------------- */
548 char szSRSId[100];
549
550 const char* pszSRID = CSLFetchNameValue( papszOptions, "SRID" );
551 if( pszSRID != nullptr )
552 snprintf( szSRSId, sizeof(szSRSId), "%s", pszSRID );
553 else if( poSRS != nullptr )
554 snprintf( szSRSId, sizeof(szSRSId), "%d", FetchSRSId( poSRS ) );
555 else
556 strcpy( szSRSId, "NULL" );
557
558 /* -------------------------------------------------------------------- */
559 /* Determine name of geometry column to use. */
560 /* -------------------------------------------------------------------- */
561 const char *pszGeometryName =
562 CSLFetchNameValue( papszOptions, "GEOMETRY_NAME" );
563 if( pszGeometryName == nullptr )
564 pszGeometryName = "ORA_GEOMETRY";
565 const bool bGeomNullable =
566 CPLFetchBool(papszOptions, "GEOMETRY_NULLABLE", true);
567
568 /* -------------------------------------------------------------------- */
569 /* Create a basic table with the FID. Also include the */
570 /* geometry if this is not a PostGIS enabled table. */
571 /* -------------------------------------------------------------------- */
572 const char *pszExpectedFIDName =
573 CPLGetConfigOption( "OCI_FID", "OGR_FID" );
574
575 OGROCIStatement oStatement( poSession );
576
577 /* -------------------------------------------------------------------- */
578 /* If geometry type is wkbNone, do not create a geometry column. */
579 /* -------------------------------------------------------------------- */
580
581 CPLString osCommand;
582 if ( CSLFetchNameValue( papszOptions, "TRUNCATE" ) == nullptr )
583 {
584 if (eType == wkbNone)
585 {
586 osCommand.Printf(
587 "CREATE TABLE \"%s\" ( "
588 "%s INTEGER PRIMARY KEY)",
589 pszSafeLayerName, pszExpectedFIDName);
590 }
591 else
592 {
593 osCommand.Printf(
594 "CREATE TABLE \"%s\" ( "
595 "%s INTEGER PRIMARY KEY, "
596 "%s %s%s )",
597 pszSafeLayerName, pszExpectedFIDName,
598 pszGeometryName, SDO_GEOMETRY,
599 (!bGeomNullable) ? " NOT NULL":"");
600 }
601
602 if (bNoLogging)
603 {
604 osCommand += CPLSPrintf(" NOLOGGING "
605 "VARRAY %s.SDO_ELEM_INFO STORE AS SECUREFILE LOB (NOCACHE NOLOGGING) "
606 "VARRAY %s.SDO_ORDINATES STORE AS SECUREFILE LOB (NOCACHE NOLOGGING) ",
607 pszGeometryName, pszGeometryName);
608 }
609
610 if( oStatement.Execute( osCommand ) != CE_None )
611 {
612 CPLFree( pszSafeLayerName );
613 return nullptr;
614 }
615 }
616
617 /* -------------------------------------------------------------------- */
618 /* Create the layer object. */
619 /* -------------------------------------------------------------------- */
620 const char *pszLoaderFile = CSLFetchNameValue(papszOptions,"LOADER_FILE");
621 OGROCIWritableLayer *poLayer;
622
623 if( pszLoaderFile == nullptr )
624 poLayer = new OGROCITableLayer( this, pszSafeLayerName, eType,
625 EQUAL(szSRSId,"NULL") ? -1 : atoi(szSRSId),
626 TRUE, bIsNewLayer);
627 else
628 poLayer =
629 new OGROCILoaderLayer( this, pszSafeLayerName,
630 pszGeometryName,
631 EQUAL(szSRSId,"NULL") ? -1 : atoi(szSRSId),
632 pszLoaderFile );
633
634 /* -------------------------------------------------------------------- */
635 /* Set various options on the layer. */
636 /* -------------------------------------------------------------------- */
637 poLayer->SetLaunderFlag( CPLFetchBool(papszOptions, "LAUNDER", false) );
638 poLayer->SetPrecisionFlag( CPLFetchBool(papszOptions, "PRECISION", true));
639 poLayer->SetDefaultStringSize( nDefaultStringSize );
640
641 const char* pszDIM = CSLFetchNameValue(papszOptions,"DIM");
642 if( pszDIM != nullptr )
643 poLayer->SetDimension( atoi(pszDIM) );
644 else if( eType != wkbNone )
645 poLayer->SetDimension( (wkbFlatten(eType) == eType) ? 2 : 3 );
646
647 poLayer->SetOptions( papszOptions );
648 if( eType != wkbNone && !bGeomNullable )
649 poLayer->GetLayerDefn()->GetGeomFieldDefn(0)->SetNullable(FALSE);
650
651 /* -------------------------------------------------------------------- */
652 /* Add layer to data source layer list. */
653 /* -------------------------------------------------------------------- */
654 papoLayers = (OGROCILayer **)
655 CPLRealloc( papoLayers, sizeof(OGROCILayer *) * (nLayers+1) );
656
657 papoLayers[nLayers++] = poLayer;
658
659 CPLFree( pszSafeLayerName );
660
661 return poLayer;
662 }
663
664 /************************************************************************/
665 /* TestCapability() */
666 /************************************************************************/
667
TestCapability(const char * pszCap)668 int OGROCIDataSource::TestCapability( const char * pszCap )
669
670 {
671 if( EQUAL(pszCap,ODsCCreateLayer) && bDSUpdate )
672 return TRUE;
673 else if( EQUAL(pszCap,ODsCDeleteLayer) && bDSUpdate )
674 return TRUE;
675 else if( EQUAL(pszCap,ODsCRandomLayerWrite) )
676 return bDSUpdate;
677 else
678 return FALSE;
679 }
680
681 /************************************************************************/
682 /* GetLayer() */
683 /************************************************************************/
684
GetLayer(int iLayer)685 OGRLayer *OGROCIDataSource::GetLayer( int iLayer )
686
687 {
688 if( iLayer < 0 || iLayer >= nLayers )
689 return nullptr;
690 else
691 return papoLayers[iLayer];
692 }
693
694 /************************************************************************/
695 /* ExecuteSQL() */
696 /************************************************************************/
697
ExecuteSQL(const char * pszSQLCommand,OGRGeometry * poSpatialFilter,const char * pszDialect)698 OGRLayer * OGROCIDataSource::ExecuteSQL( const char *pszSQLCommand,
699 OGRGeometry *poSpatialFilter,
700 const char *pszDialect )
701
702 {
703 /* -------------------------------------------------------------------- */
704 /* Use generic implementation for recognized dialects */
705 /* -------------------------------------------------------------------- */
706 if( IsGenericSQLDialect(pszDialect) )
707 return OGRDataSource::ExecuteSQL( pszSQLCommand,
708 poSpatialFilter,
709 pszDialect );
710
711 /* -------------------------------------------------------------------- */
712 /* Ensure any pending stuff is flushed to the database. */
713 /* -------------------------------------------------------------------- */
714 FlushCache();
715
716 CPLDebug( "OCI", "ExecuteSQL(%s)", pszSQLCommand );
717
718 /* -------------------------------------------------------------------- */
719 /* Special case DELLAYER: command. */
720 /* -------------------------------------------------------------------- */
721 if( STARTS_WITH_CI(pszSQLCommand, "DELLAYER:") )
722 {
723 const char *pszLayerName = pszSQLCommand + 9;
724
725 while( *pszLayerName == ' ' )
726 pszLayerName++;
727
728 DeleteLayer( pszLayerName );
729 return nullptr;
730 }
731
732 /* -------------------------------------------------------------------- */
733 /* Special case VALLAYER: command. */
734 /* -------------------------------------------------------------------- */
735 if( STARTS_WITH_CI(pszSQLCommand, "VALLAYER:") )
736 {
737 const char *pszLayerName = pszSQLCommand + 9;
738
739 while( *pszLayerName == ' ' )
740 pszLayerName++;
741
742 ValidateLayer( pszLayerName );
743 return nullptr;
744 }
745
746 /* -------------------------------------------------------------------- */
747 /* Just execute simple command. */
748 /* -------------------------------------------------------------------- */
749 if( !STARTS_WITH_CI(pszSQLCommand, "SELECT") )
750 {
751 OGROCIStatement oCommand( poSession );
752
753 oCommand.Execute( pszSQLCommand, OCI_COMMIT_ON_SUCCESS );
754
755 return nullptr;
756 }
757
758 /* -------------------------------------------------------------------- */
759 /* Otherwise instantiate a layer. */
760 /* -------------------------------------------------------------------- */
761 else
762 {
763 OGROCIStatement oCommand( poSession );
764
765 if( oCommand.Execute( pszSQLCommand, OCI_DESCRIBE_ONLY ) == CE_None )
766 return new OGROCISelectLayer( this, pszSQLCommand, &oCommand );
767 else
768 return nullptr;
769 }
770 }
771
772 /************************************************************************/
773 /* ReleaseResultSet() */
774 /************************************************************************/
775
ReleaseResultSet(OGRLayer * poLayer)776 void OGROCIDataSource::ReleaseResultSet( OGRLayer * poLayer )
777
778 {
779 delete poLayer;
780 }
781
782 /************************************************************************/
783 /* FetchSRS() */
784 /* */
785 /* Return a SRS corresponding to a particular id. Note that */
786 /* reference counting should be honoured on the returned */
787 /* OGRSpatialReference, as handles may be cached. */
788 /************************************************************************/
789
FetchSRS(int nId)790 OGRSpatialReference *OGROCIDataSource::FetchSRS( int nId )
791
792 {
793 if( nId < 0 )
794 return nullptr;
795
796 /* -------------------------------------------------------------------- */
797 /* First, we look through our SRID cache, is it there? */
798 /* -------------------------------------------------------------------- */
799 int i;
800
801 for( i = 0; i < nKnownSRID; i++ )
802 {
803 if( panSRID[i] == nId )
804 return papoSRS[i];
805 }
806
807 /* -------------------------------------------------------------------- */
808 /* Try looking up in MDSYS.CS_SRS table. */
809 /* -------------------------------------------------------------------- */
810 OGROCIStatement oStatement( GetSession() );
811 char szSelect[200], **papszResult;
812
813 snprintf( szSelect, sizeof(szSelect),
814 "SELECT WKTEXT, AUTH_SRID, AUTH_NAME FROM MDSYS.CS_SRS "
815 "WHERE SRID = %d AND WKTEXT IS NOT NULL", nId );
816
817 if( oStatement.Execute( szSelect ) != CE_None )
818 return nullptr;
819
820 papszResult = oStatement.SimpleFetchRow();
821 if( CSLCount(papszResult) < 1 )
822 return nullptr;
823
824 /* -------------------------------------------------------------------- */
825 /* Turn into a spatial reference. */
826 /* -------------------------------------------------------------------- */
827 OGRSpatialReference* poSRS = new OGRSpatialReference();
828 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
829 if( poSRS->importFromWkt( papszResult[0] ) != OGRERR_NONE )
830 {
831 delete poSRS;
832 return nullptr;
833 }
834
835 /* -------------------------------------------------------------------- */
836 /* If we have a corresponding EPSG code for this SRID, use that */
837 /* authority. */
838 /* -------------------------------------------------------------------- */
839 int bGotEPSGMapping = FALSE;
840 for( i = 0; anEPSGOracleMapping[i] != 0; i += 2 )
841 {
842 if( anEPSGOracleMapping[i] == nId )
843 {
844 poSRS->SetAuthority( poSRS->GetRoot()->GetValue(), "EPSG",
845 anEPSGOracleMapping[i+1] );
846 bGotEPSGMapping = TRUE;
847 break;
848 }
849 }
850
851 /* -------------------------------------------------------------------- */
852 /* Insert authority information, if it is available. */
853 /* -------------------------------------------------------------------- */
854 if( papszResult[1] != nullptr && atoi(papszResult[1]) != 0
855 && papszResult[2] != nullptr && strlen(papszResult[1]) != 0
856 && poSRS->GetRoot() != nullptr
857 && !bGotEPSGMapping )
858 {
859 poSRS->SetAuthority( poSRS->GetRoot()->GetValue(),
860 papszResult[2], atoi(papszResult[1]) );
861 }
862
863 /* -------------------------------------------------------------------- */
864 /* Add to the cache. */
865 /* -------------------------------------------------------------------- */
866 panSRID = (int *) CPLRealloc(panSRID,sizeof(int) * (nKnownSRID+1) );
867 papoSRS = (OGRSpatialReference **)
868 CPLRealloc(papoSRS, sizeof(void*) * (nKnownSRID + 1) );
869 panSRID[nKnownSRID] = nId;
870 papoSRS[nKnownSRID] = poSRS;
871
872 nKnownSRID++;
873
874 return poSRS;
875 }
876
877 /************************************************************************/
878 /* FetchSRSId() */
879 /* */
880 /* Fetch the id corresponding to an SRS, and if not found, add */
881 /* it to the table. */
882 /************************************************************************/
883
FetchSRSId(OGRSpatialReference * poSRS)884 int OGROCIDataSource::FetchSRSId( OGRSpatialReference * poSRS )
885
886 {
887 char *pszWKT = nullptr;
888 int nSRSId;
889
890 if( poSRS == nullptr )
891 return -1;
892
893 if( !poSRS->IsProjected() && !poSRS->IsGeographic() )
894 return -1;
895
896 /* ==================================================================== */
897 /* The first strategy is to see if we can identify it by */
898 /* authority information within the SRS. Either using ORACLE */
899 /* authority values directly, or check if there is a known */
900 /* translation for an EPSG authority code. */
901 /* ==================================================================== */
902 const char *pszAuthName = nullptr, *pszAuthCode = nullptr;
903
904 if( poSRS->IsGeographic() )
905 {
906 pszAuthName = poSRS->GetAuthorityName( "GEOGCS" );
907 pszAuthCode = poSRS->GetAuthorityCode( "GEOGCS" );
908 }
909 else if( poSRS->IsProjected() )
910 {
911 pszAuthName = poSRS->GetAuthorityName( "PROJCS" );
912 pszAuthCode = poSRS->GetAuthorityCode( "PROJCS" );
913 }
914
915 if( pszAuthName != nullptr && pszAuthCode != nullptr )
916 {
917 if( EQUAL(pszAuthName,"Oracle")
918 && atoi(pszAuthCode) != 0 )
919 return atoi(pszAuthCode);
920
921 if( EQUAL(pszAuthName,"EPSG") )
922 {
923 int i, nEPSGCode = atoi(pszAuthCode);
924
925 for( i = 0; anEPSGOracleMapping[i] != 0; i += 2 )
926 {
927 if( nEPSGCode == anEPSGOracleMapping[i+1] )
928 return anEPSGOracleMapping[i];
929 }
930 }
931 }
932
933 /* ==================================================================== */
934 /* We need to lookup the SRS in the existing Oracle CS_SRS */
935 /* table. */
936 /* ==================================================================== */
937
938 /* -------------------------------------------------------------------- */
939 /* Convert SRS into old style format (SF-SQL 1.0). */
940 /* -------------------------------------------------------------------- */
941 OGRSpatialReference *poSRS2 = poSRS->Clone();
942
943 /* -------------------------------------------------------------------- */
944 /* Convert any degree type unit names to "Decimal Degree". */
945 /* -------------------------------------------------------------------- */
946 double dfAngularUnits = poSRS2->GetAngularUnits( nullptr );
947 if( fabs(dfAngularUnits - 0.0174532925199433) < 0.0000000000000010 )
948 poSRS2->SetAngularUnits( "Decimal Degree", 0.0174532925199433 );
949
950 /* -------------------------------------------------------------------- */
951 /* Translate SRS to WKT. */
952 /* -------------------------------------------------------------------- */
953 const char* const apszOptions[] = { "FORMAT=SFSQL", nullptr };
954 if( poSRS2->exportToWkt( &pszWKT, apszOptions ) != OGRERR_NONE )
955 {
956 delete poSRS2;
957 CPLFree(pszWKT);
958 return -1;
959 }
960
961 delete poSRS2;
962
963 /* -------------------------------------------------------------------- */
964 /* Try to find in the existing table. */
965 /* -------------------------------------------------------------------- */
966 OGROCIStringBuf oCmdText;
967 OGROCIStatement oCmdStatement( GetSession() );
968 char **papszResult = nullptr;
969
970 oCmdText.Append( "SELECT SRID FROM MDSYS.CS_SRS WHERE WKTEXT = '" );
971 oCmdText.Append( pszWKT );
972 oCmdText.Append( "'" );
973
974 if( oCmdStatement.Execute( oCmdText.GetString() ) == CE_None )
975 papszResult = oCmdStatement.SimpleFetchRow() ;
976
977 /* -------------------------------------------------------------------- */
978 /* We got it! Return it. */
979 /* -------------------------------------------------------------------- */
980 if( papszResult != nullptr && papszResult[0] != nullptr && papszResult[1] == nullptr )
981 {
982 CPLFree( pszWKT );
983 return atoi( papszResult[0] );
984 }
985
986 /* ==================================================================== */
987 /* We didn't find it, so we need to define it as a new SRID at */
988 /* the end of the list of known values. */
989 /* ==================================================================== */
990
991 /* -------------------------------------------------------------------- */
992 /* Get the current maximum srid in the srs table. */
993 /* -------------------------------------------------------------------- */
994 if( oCmdStatement.Execute("SELECT MAX(SRID) FROM MDSYS.CS_SRS") == CE_None )
995 papszResult = oCmdStatement.SimpleFetchRow();
996 else
997 papszResult = nullptr;
998
999 if( papszResult != nullptr && papszResult[0] != nullptr && papszResult[1] == nullptr )
1000 nSRSId = atoi(papszResult[0]) + 1;
1001 else
1002 nSRSId = 1;
1003
1004 /* -------------------------------------------------------------------- */
1005 /* Try adding the SRS to the SRS table. */
1006 /* -------------------------------------------------------------------- */
1007 oCmdText.Clear();
1008 oCmdText.Append( "INSERT INTO MDSYS.CS_SRS (SRID, WKTEXT, CS_NAME) " );
1009 oCmdText.Appendf( 100, " VALUES (%d,'", nSRSId );
1010 oCmdText.Append( pszWKT );
1011 oCmdText.Append( "', '" );
1012 oCmdText.Append( poSRS->GetRoot()->GetChild(0)->GetValue() );
1013 oCmdText.Append( "' )" );
1014
1015 CPLFree( pszWKT );
1016
1017 if( oCmdStatement.Execute( oCmdText.GetString() ) != CE_None )
1018 return -1;
1019 else
1020 return nSRSId;
1021 }
1022
1023 /************************************************************************/
1024 /* GetLayerByName() */
1025 /************************************************************************/
1026
GetLayerByName(const char * pszNameIn)1027 OGRLayer *OGROCIDataSource::GetLayerByName( const char *pszNameIn )
1028
1029 {
1030 OGROCILayer *poLayer = nullptr;
1031 int i, count;
1032
1033 if ( !pszNameIn )
1034 return nullptr;
1035
1036 count = GetLayerCount();
1037
1038 /* first a case sensitive check */
1039 for( i = 0; i < count; i++ )
1040 {
1041 poLayer = papoLayers[i];
1042
1043 if( strcmp( pszNameIn, poLayer->GetName() ) == 0 )
1044 {
1045 return poLayer;
1046 }
1047 }
1048
1049 char *pszSafeLayerName = CPLStrdup( pszNameIn );
1050 poSession->CleanName( pszSafeLayerName );
1051
1052 /* then case insensitive and laundered */
1053 for( i = 0; i < count; i++ )
1054 {
1055 poLayer = papoLayers[i];
1056
1057 if( EQUAL( pszSafeLayerName, poLayer->GetName() ) )
1058 {
1059 break;
1060 }
1061 }
1062
1063 CPLFree( pszSafeLayerName );
1064
1065 return i < count ? poLayer : nullptr;
1066 }
1067