1 : /******************************************************************************
2 : * $Id: ogrodbcdatasource.cpp 24960 2012-09-23 18:06:25Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRODBCDataSource class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "ogr_odbc.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 :
34 : CPL_CVSID("$Id: ogrodbcdatasource.cpp 24960 2012-09-23 18:06:25Z rouault $");
35 : /************************************************************************/
36 : /* OGRODBCDataSource() */
37 : /************************************************************************/
38 :
39 0 : OGRODBCDataSource::OGRODBCDataSource()
40 :
41 : {
42 0 : pszName = NULL;
43 0 : papoLayers = NULL;
44 0 : nLayers = 0;
45 :
46 0 : nKnownSRID = 0;
47 0 : panSRID = NULL;
48 0 : papoSRS = NULL;
49 0 : }
50 :
51 : /************************************************************************/
52 : /* ~OGRODBCDataSource() */
53 : /************************************************************************/
54 :
55 0 : OGRODBCDataSource::~OGRODBCDataSource()
56 :
57 : {
58 : int i;
59 :
60 0 : CPLFree( pszName );
61 :
62 0 : for( i = 0; i < nLayers; i++ )
63 0 : delete papoLayers[i];
64 :
65 0 : CPLFree( papoLayers );
66 :
67 0 : for( i = 0; i < nKnownSRID; i++ )
68 : {
69 0 : if( papoSRS[i] != NULL )
70 0 : papoSRS[i]->Release();
71 : }
72 0 : CPLFree( panSRID );
73 0 : CPLFree( papoSRS );
74 0 : }
75 :
76 : /************************************************************************/
77 : /* OpenMDB() */
78 : /************************************************************************/
79 :
80 0 : int OGRODBCDataSource::OpenMDB( const char * pszNewName, int bUpdate )
81 : {
82 : const char *pszDSNStringTemplate =
83 0 : "DRIVER=Microsoft Access Driver (*.mdb);DBQ=%s";
84 0 : char* pszDSN = (char *) CPLMalloc(strlen(pszNewName)+strlen(pszDSNStringTemplate)+100);
85 0 : sprintf( pszDSN, pszDSNStringTemplate, pszNewName );
86 :
87 : /* -------------------------------------------------------------------- */
88 : /* Initialize based on the DSN. */
89 : /* -------------------------------------------------------------------- */
90 0 : CPLDebug( "ODBC", "EstablishSession(%s)", pszDSN );
91 :
92 0 : if( !oSession.EstablishSession( pszDSN, NULL, NULL ) )
93 : {
94 : CPLError( CE_Failure, CPLE_AppDefined,
95 : "Unable to initialize ODBC connection to DSN for %s,\n"
96 0 : "%s", pszDSN, oSession.GetLastError() );
97 0 : CPLFree( pszDSN );
98 0 : return FALSE;
99 : }
100 :
101 0 : CPLFree( pszDSN );
102 :
103 0 : pszName = CPLStrdup( pszNewName );
104 :
105 0 : bDSUpdate = bUpdate;
106 :
107 : /* -------------------------------------------------------------------- */
108 : /* Check if it is a PGeo MDB. */
109 : /* -------------------------------------------------------------------- */
110 : {
111 0 : CPLODBCStatement oStmt( &oSession );
112 :
113 0 : oStmt.Append( "SELECT TableName, FieldName, ShapeType, ExtentLeft, ExtentRight, ExtentBottom, ExtentTop, SRID, HasZ FROM GDB_GeomColumns" );
114 :
115 0 : if( oStmt.ExecuteSQL() )
116 : {
117 0 : return FALSE;
118 0 : }
119 : }
120 :
121 : /* -------------------------------------------------------------------- */
122 : /* Check if it is a Geomedia MDB. */
123 : /* -------------------------------------------------------------------- */
124 : {
125 0 : CPLODBCStatement oStmt( &oSession );
126 :
127 0 : oStmt.Append( "SELECT TableName FROM GAliasTable WHERE TableType = 'INGRFeatures'" );
128 :
129 0 : if( oStmt.ExecuteSQL() )
130 : {
131 0 : return FALSE;
132 0 : }
133 : }
134 :
135 : /* -------------------------------------------------------------------- */
136 : /* Return all tables as non-spatial tables. */
137 : /* -------------------------------------------------------------------- */
138 0 : CPLODBCStatement oTableList( &oSession );
139 :
140 0 : if( oTableList.GetTables() )
141 : {
142 0 : while( oTableList.Fetch() )
143 : {
144 0 : const char *pszSchema = oTableList.GetColData(1);
145 0 : CPLString osLayerName;
146 :
147 0 : if( pszSchema != NULL && strlen(pszSchema) > 0 )
148 : {
149 0 : osLayerName = pszSchema;
150 0 : osLayerName += ".";
151 : }
152 :
153 0 : osLayerName += oTableList.GetColData(2);
154 :
155 0 : OpenTable( osLayerName, NULL, bUpdate );
156 : }
157 :
158 0 : return TRUE;
159 : }
160 : else
161 0 : return FALSE;
162 : }
163 :
164 : /************************************************************************/
165 : /* Open() */
166 : /************************************************************************/
167 :
168 0 : int OGRODBCDataSource::Open( const char * pszNewName, int bUpdate,
169 : int bTestOpen )
170 :
171 : {
172 0 : CPLAssert( nLayers == 0 );
173 :
174 0 : if( !EQUALN(pszNewName, "ODBC:",5) && EQUAL(CPLGetExtension(pszNewName), "MDB") )
175 0 : return OpenMDB(pszNewName, bUpdate);
176 :
177 : /* -------------------------------------------------------------------- */
178 : /* Start parsing dataset name from the end of string, fetching */
179 : /* the name of spatial reference table and names for SRID and */
180 : /* SRTEXT columns first. */
181 : /* -------------------------------------------------------------------- */
182 0 : char *pszWrkName = CPLStrdup( pszNewName + 5 ); // Skip the 'ODBC:' part
183 0 : char **papszTables = NULL;
184 0 : char **papszGeomCol = NULL;
185 0 : char *pszSRSTableName = NULL;
186 0 : char *pszSRIDCol = NULL, *pszSRTextCol = NULL;
187 : char *pszDelimiter;
188 :
189 0 : if ( (pszDelimiter = strrchr( pszWrkName, ':' )) != NULL )
190 : {
191 0 : char *pszOBracket = strchr( pszDelimiter + 1, '(' );
192 :
193 0 : if( strchr(pszDelimiter,'\\') != NULL
194 : || strchr(pszDelimiter,'/') != NULL )
195 : {
196 : /*
197 : ** if there are special tokens then this isn't really
198 : ** the srs table name, so avoid further processing.
199 : */
200 : }
201 0 : else if( pszOBracket == NULL )
202 : {
203 0 : pszSRSTableName = CPLStrdup( pszDelimiter + 1 );
204 0 : *pszDelimiter = '\0';
205 : }
206 : else
207 : {
208 0 : char *pszCBracket = strchr( pszOBracket, ')' );
209 0 : if( pszCBracket != NULL )
210 0 : *pszCBracket = '\0';
211 :
212 0 : char *pszComma = strchr( pszOBracket, ',' );
213 0 : if( pszComma != NULL )
214 : {
215 0 : *pszComma = '\0';
216 0 : pszSRIDCol = CPLStrdup( pszComma + 1 );
217 : }
218 :
219 0 : *pszOBracket = '\0';
220 0 : pszSRSTableName = CPLStrdup( pszDelimiter + 1 );
221 0 : pszSRTextCol = CPLStrdup( pszOBracket + 1 );
222 :
223 0 : *pszDelimiter = '\0';
224 : }
225 : }
226 :
227 : /* -------------------------------------------------------------------- */
228 : /* Strip off any comma delimeted set of tables names to access */
229 : /* from the end of the string first. Also allow an optional */
230 : /* bracketed geometry column name after the table name. */
231 : /* -------------------------------------------------------------------- */
232 0 : while( (pszDelimiter = strrchr( pszWrkName, ',' )) != NULL )
233 : {
234 0 : char *pszOBracket = strstr( pszDelimiter + 1, "(" );
235 0 : if( pszOBracket == NULL )
236 : {
237 0 : papszTables = CSLAddString( papszTables, pszDelimiter + 1 );
238 0 : papszGeomCol = CSLAddString( papszGeomCol, "" );
239 : }
240 : else
241 : {
242 0 : char *pszCBracket = strstr(pszOBracket,")");
243 :
244 0 : if( pszCBracket != NULL )
245 0 : *pszCBracket = '\0';
246 :
247 0 : *pszOBracket = '\0';
248 0 : papszTables = CSLAddString( papszTables, pszDelimiter + 1 );
249 0 : papszGeomCol = CSLAddString( papszGeomCol, pszOBracket+1 );
250 : }
251 0 : *pszDelimiter = '\0';
252 : }
253 :
254 : /* -------------------------------------------------------------------- */
255 : /* Split out userid, password and DSN. The general form is */
256 : /* user/password@dsn. But if there are no @ characters the */
257 : /* whole thing is assumed to be a DSN. */
258 : /* -------------------------------------------------------------------- */
259 0 : char *pszUserid = NULL;
260 0 : char *pszPassword = NULL;
261 0 : char *pszDSN = NULL;
262 :
263 0 : if( strstr(pszWrkName,"@") == NULL )
264 : {
265 0 : pszDSN = CPLStrdup( pszWrkName );
266 : }
267 : else
268 : {
269 : char *pszTarget;
270 :
271 0 : pszDSN = CPLStrdup(strstr(pszWrkName, "@") + 1);
272 0 : if( *pszWrkName == '/' )
273 : {
274 0 : pszPassword = CPLStrdup(pszWrkName + 1);
275 0 : pszTarget = strstr(pszPassword,"@");
276 0 : *pszTarget = '\0';
277 : }
278 : else
279 : {
280 0 : pszUserid = CPLStrdup(pszWrkName);
281 0 : pszTarget = strstr(pszUserid,"@");
282 0 : *pszTarget = '\0';
283 :
284 0 : pszTarget = strstr(pszUserid,"/");
285 0 : if( pszTarget != NULL )
286 : {
287 0 : *pszTarget = '\0';
288 0 : pszPassword = CPLStrdup(pszTarget+1);
289 : }
290 : }
291 : }
292 :
293 0 : CPLFree( pszWrkName );
294 :
295 : /* -------------------------------------------------------------------- */
296 : /* Initialize based on the DSN. */
297 : /* -------------------------------------------------------------------- */
298 : CPLDebug( "OGR_ODBC",
299 : "EstablishSession(DSN:\"%s\", userid:\"%s\", password:\"%s\")",
300 : pszDSN, pszUserid ? pszUserid : "",
301 0 : pszPassword ? pszPassword : "" );
302 :
303 0 : if( !oSession.EstablishSession( pszDSN, pszUserid, pszPassword ) )
304 : {
305 : CPLError( CE_Failure, CPLE_AppDefined,
306 : "Unable to initialize ODBC connection to DSN for %s,\n"
307 : "%s",
308 0 : pszNewName+5, oSession.GetLastError() );
309 0 : CSLDestroy( papszTables );
310 0 : CSLDestroy( papszGeomCol );
311 0 : CPLFree( pszDSN );
312 0 : CPLFree( pszUserid );
313 0 : CPLFree( pszPassword );
314 0 : return FALSE;
315 : }
316 :
317 0 : CPLFree( pszDSN );
318 0 : CPLFree( pszUserid );
319 0 : CPLFree( pszPassword );
320 :
321 0 : pszName = CPLStrdup( pszNewName );
322 :
323 0 : bDSUpdate = bUpdate;
324 :
325 : /* -------------------------------------------------------------------- */
326 : /* If no explicit list of tables was given, check for a list in */
327 : /* a geometry_columns table. */
328 : /* -------------------------------------------------------------------- */
329 0 : if( papszTables == NULL )
330 : {
331 0 : CPLODBCStatement oStmt( &oSession );
332 :
333 : oStmt.Append( "SELECT f_table_name, f_geometry_column, geometry_type"
334 0 : " FROM geometry_columns" );
335 0 : if( oStmt.ExecuteSQL() )
336 : {
337 0 : while( oStmt.Fetch() )
338 : {
339 : papszTables =
340 0 : CSLAddString( papszTables, oStmt.GetColData(0) );
341 : papszGeomCol =
342 0 : CSLAddString( papszGeomCol, oStmt.GetColData(1) );
343 : }
344 0 : }
345 : }
346 :
347 : /* -------------------------------------------------------------------- */
348 : /* Otherwise our final resort is to return all tables as */
349 : /* non-spatial tables. */
350 : /* -------------------------------------------------------------------- */
351 0 : if( papszTables == NULL )
352 : {
353 0 : CPLODBCStatement oTableList( &oSession );
354 :
355 0 : if( oTableList.GetTables() )
356 : {
357 0 : while( oTableList.Fetch() )
358 : {
359 0 : const char *pszSchema = oTableList.GetColData(1);
360 0 : CPLString osLayerName;
361 :
362 0 : if( pszSchema != NULL && strlen(pszSchema) > 0 )
363 : {
364 0 : osLayerName = pszSchema;
365 0 : osLayerName += ".";
366 : }
367 :
368 0 : osLayerName += oTableList.GetColData(2);
369 :
370 0 : papszTables = CSLAddString( papszTables, osLayerName );
371 :
372 0 : papszGeomCol = CSLAddString(papszGeomCol,"");
373 : }
374 0 : }
375 : }
376 :
377 : /* -------------------------------------------------------------------- */
378 : /* If we have an explicit list of requested tables, use them */
379 : /* (non-spatial). */
380 : /* -------------------------------------------------------------------- */
381 0 : for( int iTable = 0;
382 0 : papszTables != NULL && papszTables[iTable] != NULL;
383 : iTable++ )
384 : {
385 0 : if( strlen(papszGeomCol[iTable]) > 0 )
386 0 : OpenTable( papszTables[iTable], papszGeomCol[iTable], bUpdate );
387 : else
388 0 : OpenTable( papszTables[iTable], NULL, bUpdate );
389 : }
390 :
391 0 : CSLDestroy( papszTables );
392 0 : CSLDestroy( papszGeomCol );
393 :
394 : /* -------------------------------------------------------------------- */
395 : /* If no explicit list of tables was given, check for a list in */
396 : /* a geometry_columns table. */
397 : /* -------------------------------------------------------------------- */
398 0 : if ( pszSRSTableName )
399 : {
400 0 : CPLODBCStatement oSRSList( &oSession );
401 :
402 0 : if ( !pszSRTextCol )
403 0 : pszSRTextCol = CPLStrdup( "srtext" );
404 0 : if ( !pszSRIDCol )
405 0 : pszSRIDCol = CPLStrdup( "srid" );
406 :
407 0 : oSRSList.Append( "SELECT " );
408 0 : oSRSList.Append( pszSRIDCol );
409 0 : oSRSList.Append( "," );
410 0 : oSRSList.Append( pszSRTextCol );
411 0 : oSRSList.Append( " FROM " );
412 0 : oSRSList.Append( pszSRSTableName );
413 :
414 : CPLDebug( "OGR_ODBC", "ExecuteSQL(%s) to read SRS table",
415 0 : oSRSList.GetCommand() );
416 0 : if ( oSRSList.ExecuteSQL() )
417 : {
418 0 : int nRows = 256; // A reasonable number of SRIDs to start from
419 0 : panSRID = (int *)CPLMalloc( nRows * sizeof(int) );
420 : papoSRS = (OGRSpatialReference **)
421 0 : CPLMalloc( nRows * sizeof(OGRSpatialReference*) );
422 :
423 0 : while ( oSRSList.Fetch() )
424 : {
425 0 : char *pszSRID = (char *) oSRSList.GetColData( pszSRIDCol );
426 0 : if ( !pszSRID )
427 0 : continue;
428 :
429 0 : char *pszSRText = (char *) oSRSList.GetColData( pszSRTextCol );
430 :
431 0 : if ( pszSRText )
432 : {
433 0 : if ( nKnownSRID > nRows )
434 : {
435 0 : nRows *= 2;
436 : panSRID = (int *)CPLRealloc( panSRID,
437 0 : nRows * sizeof(int) );
438 : papoSRS = (OGRSpatialReference **)
439 : CPLRealloc( papoSRS,
440 0 : nRows * sizeof(OGRSpatialReference*) );
441 : }
442 0 : panSRID[nKnownSRID] = atoi( pszSRID );
443 0 : papoSRS[nKnownSRID] = new OGRSpatialReference();
444 0 : if ( papoSRS[nKnownSRID]->importFromWkt( &pszSRText )
445 : != OGRERR_NONE )
446 : {
447 0 : delete papoSRS[nKnownSRID];
448 0 : continue;
449 : }
450 0 : nKnownSRID++;
451 : }
452 : }
453 0 : }
454 : }
455 :
456 0 : if ( pszSRIDCol )
457 0 : CPLFree( pszSRIDCol );
458 0 : if ( pszSRTextCol )
459 0 : CPLFree( pszSRTextCol );
460 0 : if ( pszSRSTableName )
461 0 : CPLFree( pszSRSTableName );
462 :
463 0 : return TRUE;
464 : }
465 :
466 : /************************************************************************/
467 : /* OpenTable() */
468 : /************************************************************************/
469 :
470 0 : int OGRODBCDataSource::OpenTable( const char *pszNewName,
471 : const char *pszGeomCol,
472 : int bUpdate )
473 :
474 : {
475 : /* -------------------------------------------------------------------- */
476 : /* Create the layer object. */
477 : /* -------------------------------------------------------------------- */
478 : OGRODBCTableLayer *poLayer;
479 :
480 0 : poLayer = new OGRODBCTableLayer( this );
481 :
482 0 : if( poLayer->Initialize( pszNewName, pszGeomCol ) )
483 : {
484 0 : delete poLayer;
485 0 : return FALSE;
486 : }
487 :
488 : /* -------------------------------------------------------------------- */
489 : /* Add layer to data source layer list. */
490 : /* -------------------------------------------------------------------- */
491 : papoLayers = (OGRODBCLayer **)
492 0 : CPLRealloc( papoLayers, sizeof(OGRODBCLayer *) * (nLayers+1) );
493 0 : papoLayers[nLayers++] = poLayer;
494 :
495 0 : return TRUE;
496 : }
497 :
498 : /************************************************************************/
499 : /* TestCapability() */
500 : /************************************************************************/
501 :
502 0 : int OGRODBCDataSource::TestCapability( const char * pszCap )
503 :
504 : {
505 0 : return FALSE;
506 : }
507 :
508 : /************************************************************************/
509 : /* GetLayer() */
510 : /************************************************************************/
511 :
512 0 : OGRLayer *OGRODBCDataSource::GetLayer( int iLayer )
513 :
514 : {
515 0 : if( iLayer < 0 || iLayer >= nLayers )
516 0 : return NULL;
517 : else
518 0 : return papoLayers[iLayer];
519 : }
520 : /************************************************************************/
521 : /* ExecuteSQL() */
522 : /************************************************************************/
523 :
524 0 : OGRLayer * OGRODBCDataSource::ExecuteSQL( const char *pszSQLCommand,
525 : OGRGeometry *poSpatialFilter,
526 : const char *pszDialect )
527 :
528 : {
529 : /* -------------------------------------------------------------------- */
530 : /* Use generic imlplementation for OGRSQL dialect. */
531 : /* -------------------------------------------------------------------- */
532 0 : if( pszDialect != NULL && EQUAL(pszDialect,"OGRSQL") )
533 : return OGRDataSource::ExecuteSQL( pszSQLCommand,
534 : poSpatialFilter,
535 0 : pszDialect );
536 :
537 : /* -------------------------------------------------------------------- */
538 : /* Execute statement. */
539 : /* -------------------------------------------------------------------- */
540 0 : CPLODBCStatement *poStmt = new CPLODBCStatement( &oSession );
541 :
542 0 : CPLDebug( "ODBC", "ExecuteSQL(%s) called.", pszSQLCommand );
543 0 : poStmt->Append( pszSQLCommand );
544 0 : if( !poStmt->ExecuteSQL() )
545 : {
546 : CPLError( CE_Failure, CPLE_AppDefined,
547 0 : "%s", oSession.GetLastError() );
548 0 : return NULL;
549 : }
550 :
551 : /* -------------------------------------------------------------------- */
552 : /* Are there result columns for this statement? */
553 : /* -------------------------------------------------------------------- */
554 0 : if( poStmt->GetColCount() == 0 )
555 : {
556 0 : delete poStmt;
557 0 : CPLErrorReset();
558 0 : return NULL;
559 : }
560 :
561 : /* -------------------------------------------------------------------- */
562 : /* Create a results layer. It will take ownership of the */
563 : /* statement. */
564 : /* -------------------------------------------------------------------- */
565 0 : OGRODBCSelectLayer *poLayer = NULL;
566 :
567 0 : poLayer = new OGRODBCSelectLayer( this, poStmt );
568 :
569 0 : if( poSpatialFilter != NULL )
570 0 : poLayer->SetSpatialFilter( poSpatialFilter );
571 :
572 0 : return poLayer;
573 : }
574 :
575 : /************************************************************************/
576 : /* ReleaseResultSet() */
577 : /************************************************************************/
578 :
579 0 : void OGRODBCDataSource::ReleaseResultSet( OGRLayer * poLayer )
580 :
581 : {
582 0 : delete poLayer;
583 0 : }
|