1 /******************************************************************************
2  * $Id: ogrocisession.cpp 28481 2015-02-13 17:11:15Z rouault $
3  *
4  * Project:  Oracle Spatial Driver
5  * Purpose:  Implementation of OGROCISession, which encapsulates much of the
6  *           direct access to OCI.
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 
34 CPL_CVSID("$Id: ogrocisession.cpp 28481 2015-02-13 17:11:15Z rouault $");
35 
36 /************************************************************************/
37 /*                          OGRGetOCISession()                          */
38 /************************************************************************/
39 
OGRGetOCISession(const char * pszUserid,const char * pszPassword,const char * pszDatabase)40 OGROCISession * OGRGetOCISession( const char *pszUserid,
41                                   const char *pszPassword,
42                                   const char *pszDatabase )
43 
44 {
45     OGROCISession *poSession;
46 
47     poSession = new OGROCISession();
48     if( poSession->EstablishSession( pszUserid, pszPassword, pszDatabase ) )
49         return poSession;
50     else
51     {
52         delete poSession;
53         return NULL;
54     }
55 }
56 
57 /************************************************************************/
58 /*                           OGROCISession()                            */
59 /************************************************************************/
60 
OGROCISession()61 OGROCISession::OGROCISession()
62 
63 {
64     hEnv = NULL;
65     hError = NULL;
66     hSvcCtx = NULL;
67     hServer = NULL;
68     hSession = NULL;
69     hDescribe = NULL;
70     hGeometryTDO = NULL;
71     hOrdinatesTDO = NULL;
72     hElemInfoTDO = NULL;
73     pszUserid = NULL;
74     pszPassword = NULL;
75     pszDatabase = NULL;
76 }
77 
78 /************************************************************************/
79 /*                           ~OGROCISession()                           */
80 /************************************************************************/
81 
~OGROCISession()82 OGROCISession::~OGROCISession()
83 
84 {
85     if( hDescribe != NULL )
86         OCIHandleFree((dvoid *)hDescribe, (ub4)OCI_HTYPE_DESCRIBE);
87 
88     if( hSvcCtx != NULL )
89     {
90         OCISessionEnd(hSvcCtx, hError, hSession, (ub4) 0);
91 
92         if( hSvcCtx && hError)
93             OCIServerDetach(hServer, hError, (ub4) OCI_DEFAULT);
94 
95         if( hServer )
96             OCIHandleFree((dvoid *) hServer, (ub4) OCI_HTYPE_SERVER);
97 
98         if( hSvcCtx )
99             OCIHandleFree((dvoid *) hSvcCtx, (ub4) OCI_HTYPE_SVCCTX);
100 
101         if( hError )
102             OCIHandleFree((dvoid *) hError, (ub4) OCI_HTYPE_ERROR);
103 
104         if( hSession )
105             OCIHandleFree((dvoid *) hSession, (ub4) OCI_HTYPE_SESSION);
106 
107         if( hEnv )
108             OCIHandleFree((dvoid *) hEnv, (ub4) OCI_HTYPE_ENV);
109     }
110 
111     CPLFree( pszUserid );
112     CPLFree( pszPassword );
113     CPLFree( pszDatabase );
114 }
115 
116 /************************************************************************/
117 /*                          EstablishSession()                          */
118 /************************************************************************/
119 
EstablishSession(const char * pszUserid,const char * pszPassword,const char * pszDatabase)120 int OGROCISession::EstablishSession( const char *pszUserid,
121                                      const char *pszPassword,
122                                      const char *pszDatabase )
123 
124 {
125 /* -------------------------------------------------------------------- */
126 /*      Operational Systems's authentication option                     */
127 /* -------------------------------------------------------------------- */
128 
129     ub4 eCred = OCI_CRED_RDBMS;
130 
131     if( EQUAL(pszDatabase, "") &&
132         EQUAL(pszPassword, "") &&
133         EQUAL(pszUserid, "/") )
134     {
135         eCred = OCI_CRED_EXT;
136     }
137 
138 /* -------------------------------------------------------------------- */
139 /*      Initialize Environment handler                                  */
140 /* -------------------------------------------------------------------- */
141 
142     if( Failed( OCIInitialize((ub4) (OCI_DEFAULT | OCI_OBJECT), (dvoid *)0,
143                 (dvoid * (*)(dvoid *, size_t)) 0,
144                 (dvoid * (*)(dvoid *, dvoid *, size_t))0,
145                 (void (*)(dvoid *, dvoid *)) 0 ) ) )
146     {
147         return FALSE;
148     }
149 
150     if( Failed( OCIEnvInit( (OCIEnv **) &hEnv, OCI_DEFAULT, (size_t) 0,
151                 (dvoid **) 0 ) ) )
152     {
153         return FALSE;
154     }
155 
156     if( Failed( OCIHandleAlloc( (dvoid *) hEnv, (dvoid **) &hError,
157                 OCI_HTYPE_ERROR, (size_t) 0, (dvoid **) 0) ) )
158     {
159         return FALSE;
160     }
161 
162 /* -------------------------------------------------------------------- */
163 /*      Initialize Server Context                                       */
164 /* -------------------------------------------------------------------- */
165 
166     if( Failed( OCIHandleAlloc( (dvoid *) hEnv, (dvoid **) &hServer,
167                 OCI_HTYPE_SERVER, (size_t) 0, (dvoid **) 0) ) )
168     {
169         return FALSE;
170     }
171 
172     if( Failed( OCIHandleAlloc( (dvoid *) hEnv, (dvoid **) &hSvcCtx,
173                 OCI_HTYPE_SVCCTX, (size_t) 0, (dvoid **) 0) ) )
174     {
175         return FALSE;
176     }
177 
178     if( Failed( OCIServerAttach( hServer, hError, (text*) pszDatabase,
179                 strlen((char*) pszDatabase), 0) ) )
180     {
181         return FALSE;
182     }
183 
184 /* -------------------------------------------------------------------- */
185 /*      Initialize Service Context                                      */
186 /* -------------------------------------------------------------------- */
187 
188     if( Failed( OCIAttrSet( (dvoid *) hSvcCtx, OCI_HTYPE_SVCCTX, (dvoid *)hServer,
189                 (ub4) 0, OCI_ATTR_SERVER, (OCIError *) hError) ) )
190     {
191         return FALSE;
192     }
193 
194     if( Failed( OCIHandleAlloc((dvoid *) hEnv, (dvoid **)&hSession,
195                 (ub4) OCI_HTYPE_SESSION, (size_t) 0, (dvoid **) 0) ) )
196     {
197         return FALSE;
198     }
199 
200     if( Failed( OCIAttrSet((dvoid *) hSession, (ub4) OCI_HTYPE_SESSION,
201                 (dvoid *) pszUserid, (ub4) strlen((char *) pszUserid),
202                 (ub4) OCI_ATTR_USERNAME, hError) ) )
203     {
204         return FALSE;
205     }
206 
207     if( Failed( OCIAttrSet((dvoid *) hSession, (ub4) OCI_HTYPE_SESSION,
208                 (dvoid *) pszPassword, (ub4) strlen((char *) pszPassword),
209                 (ub4) OCI_ATTR_PASSWORD, hError) ) )
210     {
211         return FALSE;
212     }
213 
214 /* -------------------------------------------------------------------- */
215 /*      Initialize Session                                              */
216 /* -------------------------------------------------------------------- */
217 
218     if( Failed( OCISessionBegin(hSvcCtx, hError, hSession, eCred,
219                 (ub4) OCI_DEFAULT) ) )
220     {
221         CPLDebug("OCI", "OCISessionBegin() failed to intialize session");
222         return FALSE;
223     }
224 
225 /* -------------------------------------------------------------------- */
226 /*      Initialize Service                                              */
227 /* -------------------------------------------------------------------- */
228 
229     if( Failed( OCIAttrSet((dvoid *) hSvcCtx, (ub4) OCI_HTYPE_SVCCTX,
230                 (dvoid *) hSession, (ub4) 0,
231                 (ub4) OCI_ATTR_SESSION, hError) ) )
232     {
233         return FALSE;
234     }
235 
236 /* -------------------------------------------------------------------- */
237 /*      Create a describe handle.                                       */
238 /* -------------------------------------------------------------------- */
239 
240     if( Failed(
241         OCIHandleAlloc( hEnv, (dvoid **) &hDescribe, (ub4)OCI_HTYPE_DESCRIBE,
242                         (size_t)0, (dvoid **)0 ),
243         "OCIHandleAlloc(Describe)" ) )
244         return FALSE;
245 
246 /* -------------------------------------------------------------------- */
247 /*      Try to get the MDSYS.SDO_GEOMETRY type object.                  */
248 /* -------------------------------------------------------------------- */
249     /* If we have no MDSYS.SDO_GEOMETRY then we consider we are
250         working along with the VRT driver and access non spatial tables.
251         See #2202 for more details (Tamas Szekeres)*/
252     if (OCIDescribeAny(hSvcCtx, hError,
253                        (text *) SDO_GEOMETRY, (ub4) strlen(SDO_GEOMETRY),
254                        OCI_OTYPE_NAME, (ub1) OCI_DEFAULT, (ub1)OCI_PTYPE_TYPE,
255                        hDescribe ) != OCI_ERROR)
256     {
257         hGeometryTDO = PinTDO( SDO_GEOMETRY );
258         if( hGeometryTDO == NULL )
259             return FALSE;
260 
261 /* -------------------------------------------------------------------- */
262 /*      Try to get the MDSYS.SDO_ORDINATE_ARRAY type object.            */
263 /* -------------------------------------------------------------------- */
264         hOrdinatesTDO = PinTDO( "MDSYS.SDO_ORDINATE_ARRAY" );
265         if( hOrdinatesTDO == NULL )
266             return FALSE;
267 
268 /* -------------------------------------------------------------------- */
269 /*      Try to get the MDSYS.SDO_ELEM_INFO_ARRAY type object.           */
270 /* -------------------------------------------------------------------- */
271         hElemInfoTDO = PinTDO( "MDSYS.SDO_ELEM_INFO_ARRAY" );
272         if( hElemInfoTDO == NULL )
273             return FALSE;
274     }
275 /* -------------------------------------------------------------------- */
276 /*      Record information about the session.                           */
277 /* -------------------------------------------------------------------- */
278     this->pszUserid = CPLStrdup(pszUserid);
279     this->pszPassword = CPLStrdup(pszPassword);
280     this->pszDatabase = CPLStrdup(pszDatabase);
281 
282 /* -------------------------------------------------------------------- */
283 /*      Setting upt the OGR compatible time formating rules.            */
284 /* -------------------------------------------------------------------- */
285     OGROCIStatement     oSetNLSTimeFormat( this );
286     if( oSetNLSTimeFormat.Execute( "ALTER SESSION SET NLS_DATE_FORMAT='YYYY/MM/DD' \
287         NLS_TIME_FORMAT='HH24:MI:SS' NLS_TIME_TZ_FORMAT='HH24:MI:SS TZHTZM' \
288         NLS_TIMESTAMP_FORMAT='YYYY/MM/DD HH24:MI:SS' \
289         NLS_TIMESTAMP_TZ_FORMAT='YYYY/MM/DD HH24:MI:SS TZHTZM' \
290         NLS_NUMERIC_CHARACTERS = '. '" ) != CE_None )
291         return OGRERR_FAILURE;
292 
293     return TRUE;
294 }
295 
296 /************************************************************************/
297 /*                               Failed()                               */
298 /************************************************************************/
299 
Failed(sword nStatus,const char * pszFunction)300 int OGROCISession::Failed( sword nStatus, const char *pszFunction )
301 
302 {
303     if( pszFunction == NULL )
304         pszFunction = "<unnamed>";
305     if( nStatus == OCI_ERROR )
306     {
307         sb4  nErrCode = 0;
308         char szErrorMsg[10000];
309 
310         szErrorMsg[0] = '\0';
311         if( hError != NULL )
312         {
313             OCIErrorGet( (dvoid *) hError, (ub4) 1, NULL, &nErrCode,
314                          (text *) szErrorMsg, (ub4) sizeof(szErrorMsg),
315                          OCI_HTYPE_ERROR );
316         }
317         szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
318 
319         CPLError( CE_Failure, CPLE_AppDefined,
320                   "%s in %s", szErrorMsg, pszFunction );
321         return TRUE;
322     }
323     else if( nStatus == OCI_NEED_DATA )
324     {
325         CPLError( CE_Failure, CPLE_AppDefined,
326                   "OCI_NEED_DATA" );
327         return TRUE;
328     }
329     else if( nStatus == OCI_INVALID_HANDLE )
330     {
331         CPLError( CE_Failure, CPLE_AppDefined,
332                   "OCI_INVALID_HANDLE in %s", pszFunction );
333         return TRUE;
334     }
335     else if( nStatus == OCI_STILL_EXECUTING )
336     {
337         CPLError( CE_Failure, CPLE_AppDefined,
338                   "OCI_STILL_EXECUTING in %s", pszFunction );
339         return TRUE;
340     }
341     else if( nStatus == OCI_CONTINUE )
342     {
343         CPLError( CE_Failure, CPLE_AppDefined,
344                   "OCI_CONTINUE in %s", pszFunction );
345         return TRUE;
346     }
347     else
348         return FALSE;
349 }
350 
351 /************************************************************************/
352 /*                            GetParmInfo()                             */
353 /************************************************************************/
354 
355 CPLErr
GetParmInfo(OCIParam * hParmDesc,OGRFieldDefn * poOGRDefn,ub2 * pnOCIType,ub4 * pnOCILen)356 OGROCISession::GetParmInfo( OCIParam *hParmDesc, OGRFieldDefn *poOGRDefn,
357                             ub2 *pnOCIType, ub4 *pnOCILen )
358 
359 {
360     ub2 nOCIType, nOCILen;
361     ub4 nColLen;
362     ub1 bOCINull;
363     char *pszColName;
364     char szTermColName[128];
365 
366 /* -------------------------------------------------------------------- */
367 /*      Get basic parameter details.                                    */
368 /* -------------------------------------------------------------------- */
369     if( Failed(
370         OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM,
371                     (dvoid **)&nOCIType, 0, OCI_ATTR_DATA_TYPE, hError ),
372         "OCIAttrGet(Type)" ) )
373         return CE_Failure;
374 
375     if( Failed(
376         OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM,
377                     (dvoid **)&nOCILen, 0, OCI_ATTR_DATA_SIZE, hError ),
378         "OCIAttrGet(Size)" ) )
379         return CE_Failure;
380 
381     if( Failed(
382         OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM, (dvoid **)&pszColName,
383                     &nColLen, OCI_ATTR_NAME, hError ),
384         "OCIAttrGet(Name)") )
385         return CE_Failure;
386 
387     if( Failed(
388         OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM, (dvoid **)&bOCINull,
389                     0, OCI_ATTR_IS_NULL, hError ),
390         "OCIAttrGet(Null)") )
391         return CE_Failure;
392 
393     if( nColLen >= sizeof(szTermColName) )
394     {
395         CPLError( CE_Failure, CPLE_AppDefined,
396                   "Column length (%d) longer than column name buffer (%d) in\n"
397                   "OGROCISession::GetParmInfo()",
398                   nColLen, (int) sizeof(szTermColName) );
399         return CE_Failure;
400     }
401 
402     strncpy( szTermColName, pszColName, nColLen );
403     szTermColName[nColLen] = '\0';
404 
405     poOGRDefn->SetName( szTermColName );
406     poOGRDefn->SetNullable( bOCINull );
407 
408 /* -------------------------------------------------------------------- */
409 /*      Attempt to classify as an OGRType.                              */
410 /* -------------------------------------------------------------------- */
411     switch( nOCIType )
412     {
413         case SQLT_CHR:
414         case SQLT_AFC: /* CHAR(), NCHAR() */
415             poOGRDefn->SetType( OFTString );
416             if( nOCILen <= 4000 )
417                 poOGRDefn->SetWidth( nOCILen );
418             break;
419 
420         case SQLT_NUM:
421         {
422             // NOTE: OCI docs say this should be ub1 type, but we have
423             // determined that oracle is actually returning a short so we
424             // use that type and try to compensate for possible problems by
425             // initializing, and dividing by 256 if it is large.
426             unsigned short byPrecision = 0;
427             sb1  nScale;
428 
429             if( Failed(
430                 OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM, (dvoid **)&byPrecision,
431                             0, OCI_ATTR_PRECISION, hError ),
432                 "OCIAttrGet(Precision)" ) )
433                 return CE_Failure;
434             if( Failed(
435                 OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM, (dvoid **)&nScale,
436                             0, OCI_ATTR_SCALE, hError ),
437                 "OCIAttrGet(Scale)") )
438                 return CE_Failure;
439 #ifdef notdef
440             CPLDebug( "OCI", "%s: Scale=%d, Precision=%d",
441                       szTermColName, nScale, byPrecision );
442 #endif
443             if( byPrecision > 255 )
444                 byPrecision = byPrecision / 256;
445 
446             if( nScale < 0 )
447                 poOGRDefn->SetType( OFTReal );
448             else if( nScale > 0 )
449             {
450                 poOGRDefn->SetType( OFTReal );
451                 poOGRDefn->SetWidth( byPrecision );
452                 poOGRDefn->SetPrecision( nScale );
453             }
454             else if( byPrecision < 38 )
455             {
456                 poOGRDefn->SetType( (byPrecision < 10) ? OFTInteger : OFTInteger64 );
457                 poOGRDefn->SetWidth( byPrecision );
458             }
459             else
460             {
461                 poOGRDefn->SetType( OFTInteger64 );
462             }
463         }
464         break;
465 
466         case SQLT_DAT:
467         case SQLT_DATE:
468             poOGRDefn->SetType( OFTDate );
469             break;
470         case SQLT_TIMESTAMP:
471         case SQLT_TIMESTAMP_TZ:
472         case SQLT_TIMESTAMP_LTZ:
473         case SQLT_TIME:
474         case SQLT_TIME_TZ:
475             poOGRDefn->SetType( OFTDateTime );
476             break;
477 
478         case SQLT_RID:
479         case SQLT_BIN:
480         case SQLT_LBI:
481         case 111: /* REF */
482         case SQLT_CLOB:
483         case SQLT_BLOB:
484         case SQLT_FILE:
485         case 208: /* UROWID */
486             poOGRDefn->SetType( OFTBinary );
487             break;
488 
489         default:
490             poOGRDefn->SetType( OFTBinary );
491             break;
492     }
493 
494     if( pnOCIType != NULL )
495         *pnOCIType = nOCIType;
496 
497     if( pnOCILen != NULL )
498         *pnOCILen = nOCILen;
499 
500     return CE_None;
501 }
502 
503 /************************************************************************/
504 /*                             CleanName()                              */
505 /*                                                                      */
506 /*      Modify a name in-place to be a well formed Oracle name.         */
507 /************************************************************************/
508 
CleanName(char * pszName)509 void OGROCISession::CleanName( char * pszName )
510 
511 {
512     int   i;
513 
514     if( strlen(pszName) > 30 )
515         pszName[30] = '\0';
516 
517     for( i = 0; pszName[i] != '\0'; i++ )
518     {
519         pszName[i] = toupper(pszName[i]);
520 
521         if( (pszName[i] < '0' || pszName[i] > '9')
522             && (pszName[i] < 'A' || pszName[i] > 'Z')
523             && pszName[i] != '_' )
524             pszName[i] = '_';
525     }
526 }
527 
528 /************************************************************************/
529 /*                               PinTDO()                               */
530 /*                                                                      */
531 /*      Fetch a Type Description Object for the named type.             */
532 /************************************************************************/
533 
PinTDO(const char * pszType)534 OCIType *OGROCISession::PinTDO( const char *pszType )
535 
536 {
537     OCIParam *hGeomParam = NULL;
538     OCIRef *hGeomTypeRef = NULL;
539     OCIType *hPinnedTDO = NULL;
540 
541     if( Failed(
542         OCIDescribeAny(hSvcCtx, hError,
543                        (text *) pszType, (ub4) strlen(pszType),
544                        OCI_OTYPE_NAME, (ub1)1, (ub1)OCI_PTYPE_TYPE,
545                        hDescribe ),
546         "GetTDO()->OCIDescribeAny()" ) )
547         return NULL;
548 
549     if( Failed(
550         OCIAttrGet((dvoid *)hDescribe, (ub4)OCI_HTYPE_DESCRIBE,
551                    (dvoid *)&hGeomParam, (ub4 *)0, (ub4)OCI_ATTR_PARAM,
552                    hError), "GetTDO()->OCIGetAttr(ATTR_PARAM)") )
553         return NULL;
554 
555     if( Failed(
556         OCIAttrGet((dvoid *)hGeomParam, (ub4)OCI_DTYPE_PARAM,
557                    (dvoid *)&hGeomTypeRef, (ub4 *)0, (ub4)OCI_ATTR_REF_TDO,
558                    hError), "GetTDO()->OCIAttrGet(ATTR_REF_TDO)" ) )
559         return NULL;
560 
561     if( Failed(
562         OCIObjectPin(hEnv, hError, hGeomTypeRef, (OCIComplexObject *)0,
563                      OCI_PIN_ANY, OCI_DURATION_SESSION,
564                      OCI_LOCK_NONE, (dvoid **)&hPinnedTDO ),
565         "GetTDO()->OCIObjectPin()" ) )
566         return NULL;
567 
568     return hPinnedTDO;
569 }
570