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