1 : /******************************************************************************
2 : * $Id: ogrpgdumpdatasource.cpp 19705 2010-05-14 19:42:02Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRPGDumpDataSource class.
6 : * Author: Even Rouault, <even dot rouault at mines dash paris dot org>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010, Even Rouault
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 <string.h>
31 : #include "ogr_pgdump.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_string.h"
34 :
35 : CPL_CVSID("$Id: ogrpgdumpdatasource.cpp 19705 2010-05-14 19:42:02Z rouault $");
36 :
37 : /************************************************************************/
38 : /* OGRPGDumpDataSource() */
39 : /************************************************************************/
40 :
41 : OGRPGDumpDataSource::OGRPGDumpDataSource(const char* pszName,
42 3 : char** papszOptions)
43 :
44 : {
45 3 : nLayers = 0;
46 3 : papoLayers = NULL;
47 3 : this->pszName = CPLStrdup(pszName);
48 3 : bTriedOpen = FALSE;
49 3 : fp = NULL;
50 3 : bInTransaction = FALSE;
51 3 : poLayerInCopyMode = NULL;
52 :
53 3 : const char *pszCRLFFormat = CSLFetchNameValue( papszOptions, "LINEFORMAT");
54 :
55 : int bUseCRLF;
56 3 : if( pszCRLFFormat == NULL )
57 : {
58 : #ifdef WIN32
59 : bUseCRLF = TRUE;
60 : #else
61 1 : bUseCRLF = FALSE;
62 : #endif
63 : }
64 2 : else if( EQUAL(pszCRLFFormat,"CRLF") )
65 1 : bUseCRLF = TRUE;
66 1 : else if( EQUAL(pszCRLFFormat,"LF") )
67 1 : bUseCRLF = FALSE;
68 : else
69 : {
70 : CPLError( CE_Warning, CPLE_AppDefined,
71 : "LINEFORMAT=%s not understood, use one of CRLF or LF.",
72 0 : pszCRLFFormat );
73 : #ifdef WIN32
74 : bUseCRLF = TRUE;
75 : #else
76 0 : bUseCRLF = FALSE;
77 : #endif
78 : }
79 3 : pszEOL = (bUseCRLF) ? "\r\n" : "\n";
80 3 : }
81 :
82 : /************************************************************************/
83 : /* ~OGRPGDumpDataSource() */
84 : /************************************************************************/
85 :
86 3 : OGRPGDumpDataSource::~OGRPGDumpDataSource()
87 :
88 : {
89 : int i;
90 :
91 3 : if (fp)
92 : {
93 3 : Commit();
94 3 : VSIFCloseL(fp);
95 3 : fp = NULL;
96 : }
97 :
98 6 : for(i=0;i<nLayers;i++)
99 3 : delete papoLayers[i];
100 3 : CPLFree(papoLayers);
101 3 : CPLFree(pszName);
102 3 : }
103 :
104 : /************************************************************************/
105 : /* StartTransaction() */
106 : /************************************************************************/
107 :
108 3 : void OGRPGDumpDataSource::StartTransaction()
109 : {
110 3 : if (bInTransaction)
111 0 : return;
112 3 : bInTransaction = TRUE;
113 3 : Log("BEGIN");
114 : }
115 :
116 : /************************************************************************/
117 : /* Commit() */
118 : /************************************************************************/
119 :
120 6 : void OGRPGDumpDataSource::Commit()
121 : {
122 6 : EndCopy();
123 :
124 6 : if (!bInTransaction)
125 3 : return;
126 3 : bInTransaction = FALSE;
127 3 : Log("COMMIT");
128 : }
129 :
130 : /************************************************************************/
131 : /* LaunderName() */
132 : /************************************************************************/
133 :
134 15 : char *OGRPGDumpDataSource::LaunderName( const char *pszSrcName )
135 :
136 : {
137 15 : char *pszSafeName = CPLStrdup( pszSrcName );
138 :
139 108 : for( int i = 0; pszSafeName[i] != '\0'; i++ )
140 : {
141 93 : pszSafeName[i] = (char) tolower( pszSafeName[i] );
142 93 : if( pszSafeName[i] == '-' || pszSafeName[i] == '#' )
143 0 : pszSafeName[i] = '_';
144 : }
145 :
146 15 : if( strcmp(pszSrcName,pszSafeName) != 0 )
147 : CPLDebug("PG","LaunderName('%s') -> '%s'",
148 12 : pszSrcName, pszSafeName);
149 :
150 15 : return pszSafeName;
151 : }
152 :
153 : /************************************************************************/
154 : /* CreateLayer() */
155 : /************************************************************************/
156 :
157 : OGRLayer *
158 : OGRPGDumpDataSource::CreateLayer( const char * pszLayerNameIn,
159 : OGRSpatialReference *poSRS,
160 : OGRwkbGeometryType eType,
161 3 : char ** papszOptions )
162 :
163 : {
164 3 : CPLString osCommand;
165 3 : const char *pszGeomType = NULL;
166 3 : char *pszLayerName = NULL;
167 3 : const char *pszTableName = NULL;
168 3 : char *pszSchemaName = NULL;
169 3 : int nDimension = 3;
170 3 : const char *pszFIDColumn = "OGC_FID";
171 3 : int bHavePostGIS = TRUE;
172 :
173 3 : if (pszLayerNameIn == NULL)
174 0 : return NULL;
175 :
176 3 : if (strncmp(pszLayerNameIn, "pg", 2) == 0)
177 : {
178 : CPLError(CE_Warning, CPLE_AppDefined,
179 0 : "The layer name should not begin by 'pg' as it is a reserved prefix");
180 : }
181 :
182 : //bHavePostGIS = CSLFetchBoolean(papszOptions,"POSTGIS", TRUE);
183 :
184 3 : if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) )
185 : {
186 3 : pszLayerName = LaunderName( pszLayerNameIn );
187 :
188 : }
189 : else
190 0 : pszLayerName = CPLStrdup( pszLayerNameIn );
191 :
192 3 : int bCreateTable = CSLFetchBoolean(papszOptions,"CREATE_TABLE", TRUE);
193 :
194 3 : if (eType == wkbNone)
195 1 : bHavePostGIS = FALSE;
196 2 : else if( wkbFlatten(eType) == eType )
197 2 : nDimension = 2;
198 :
199 3 : if( CSLFetchNameValue( papszOptions, "DIM") != NULL )
200 1 : nDimension = atoi(CSLFetchNameValue( papszOptions, "DIM"));
201 :
202 : /* Postgres Schema handling:
203 : Extract schema name from input layer name or passed with -lco SCHEMA.
204 : Set layer name to "schema.table" or to "table" if schema == current_schema()
205 : Usage without schema name is backwards compatible
206 : */
207 3 : pszTableName = strstr(pszLayerName,".");
208 3 : if ( pszTableName != NULL )
209 : {
210 0 : int length = pszTableName - pszLayerName;
211 0 : pszSchemaName = (char*)CPLMalloc(length+1);
212 0 : strncpy(pszSchemaName, pszLayerName, length);
213 0 : pszSchemaName[length] = '\0';
214 0 : ++pszTableName; //skip "."
215 : }
216 : else
217 : {
218 3 : pszSchemaName = NULL;
219 3 : pszTableName = pszLayerName;
220 : }
221 :
222 3 : Commit();
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* Set the default schema for the layers. */
226 : /* -------------------------------------------------------------------- */
227 3 : if( CSLFetchNameValue( papszOptions, "SCHEMA" ) != NULL )
228 : {
229 2 : CPLFree(pszSchemaName);
230 2 : pszSchemaName = CPLStrdup(CSLFetchNameValue( papszOptions, "SCHEMA" ));
231 2 : osCommand.Printf("CREATE SCHEMA \"%s\"", pszSchemaName);
232 2 : Log(osCommand);
233 : }
234 :
235 3 : if ( pszSchemaName == NULL)
236 : {
237 1 : pszSchemaName = CPLStrdup("public");
238 : }
239 :
240 : /* -------------------------------------------------------------------- */
241 : /* Do we already have this layer? */
242 : /* -------------------------------------------------------------------- */
243 : int iLayer;
244 :
245 3 : for( iLayer = 0; iLayer < nLayers; iLayer++ )
246 : {
247 0 : if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) )
248 : {
249 : CPLError( CE_Failure, CPLE_AppDefined,
250 : "Layer %s already exists, CreateLayer failed.\n",
251 0 : pszLayerName );
252 0 : CPLFree( pszLayerName );
253 0 : CPLFree( pszSchemaName );
254 0 : return NULL;
255 : }
256 : }
257 :
258 :
259 3 : if (bCreateTable)
260 : {
261 3 : osCommand.Printf("DROP TABLE \"%s\".\"%s\" CASCADE", pszSchemaName, pszTableName );
262 3 : Log(osCommand);
263 : }
264 :
265 : /* -------------------------------------------------------------------- */
266 : /* Handle the GEOM_TYPE option. */
267 : /* -------------------------------------------------------------------- */
268 3 : pszGeomType = CSLFetchNameValue( papszOptions, "GEOM_TYPE" );
269 3 : if( pszGeomType == NULL )
270 : {
271 3 : pszGeomType = "geometry";
272 : }
273 :
274 3 : if( !EQUAL(pszGeomType,"geometry") && !EQUAL(pszGeomType, "geography"))
275 : {
276 : CPLError( CE_Failure, CPLE_AppDefined,
277 : "GEOM_TYPE in PostGIS enabled databases must be 'geometry' or 'geography'.\n"
278 : "Creation of layer %s with GEOM_TYPE %s has failed.",
279 0 : pszLayerName, pszGeomType );
280 0 : CPLFree( pszLayerName );
281 0 : CPLFree( pszSchemaName );
282 0 : return NULL;
283 : }
284 :
285 : /* -------------------------------------------------------------------- */
286 : /* Try to get the SRS Id of this spatial reference system, */
287 : /* adding tot the srs table if needed. */
288 : /* -------------------------------------------------------------------- */
289 3 : int nSRSId = -1;
290 :
291 3 : if( CSLFetchNameValue( papszOptions, "SRID") != NULL )
292 1 : nSRSId = atoi(CSLFetchNameValue( papszOptions, "SRID"));
293 : else
294 : {
295 2 : if (poSRS)
296 : {
297 0 : const char* pszAuthorityName = poSRS->GetAuthorityName(NULL);
298 0 : if( pszAuthorityName != NULL && EQUAL( pszAuthorityName, "EPSG" ) )
299 : {
300 : /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */
301 0 : nSRSId = atoi( poSRS->GetAuthorityCode(NULL) );
302 : }
303 : else
304 : {
305 0 : const char* pszGeogCSName = poSRS->GetAttrValue("GEOGCS");
306 0 : if (pszGeogCSName != NULL && EQUAL(pszGeogCSName, "GCS_WGS_1984"))
307 0 : nSRSId = 4326;
308 : }
309 : }
310 : }
311 :
312 : const char *pszGeometryType;
313 3 : switch( wkbFlatten(eType) )
314 : {
315 : case wkbPoint:
316 0 : pszGeometryType = "POINT";
317 0 : break;
318 :
319 : case wkbLineString:
320 0 : pszGeometryType = "LINESTRING";
321 0 : break;
322 :
323 : case wkbPolygon:
324 1 : pszGeometryType = "POLYGON";
325 1 : break;
326 :
327 : case wkbMultiPoint:
328 0 : pszGeometryType = "MULTIPOINT";
329 0 : break;
330 :
331 : case wkbMultiLineString:
332 0 : pszGeometryType = "MULTILINESTRING";
333 0 : break;
334 :
335 : case wkbMultiPolygon:
336 0 : pszGeometryType = "MULTIPOLYGON";
337 0 : break;
338 :
339 : case wkbGeometryCollection:
340 0 : pszGeometryType = "GEOMETRYCOLLECTION";
341 0 : break;
342 :
343 : default:
344 2 : pszGeometryType = "GEOMETRY";
345 : break;
346 : }
347 :
348 3 : const char *pszGFldName = NULL;
349 3 : if( bHavePostGIS && !EQUAL(pszGeomType, "geography"))
350 : {
351 2 : if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL )
352 1 : pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
353 : else
354 1 : pszGFldName = "wkb_geometry";
355 :
356 : /* Sometimes there is an old cruft entry in the geometry_columns
357 : * table if things were not properly cleaned up before. We make
358 : * an effort to clean out such cruft.
359 : */
360 : osCommand.Printf(
361 : "DELETE FROM geometry_columns WHERE f_table_name = '%s' AND f_table_schema = '%s'",
362 2 : pszTableName, pszSchemaName );
363 2 : if (bCreateTable)
364 2 : Log(osCommand);
365 : }
366 :
367 :
368 3 : StartTransaction();
369 :
370 : /* -------------------------------------------------------------------- */
371 : /* Create a basic table with the FID. Also include the */
372 : /* geometry if this is not a PostGIS enabled table. */
373 : /* -------------------------------------------------------------------- */
374 :
375 3 : CPLString osCreateTable;
376 : int bTemporary = CSLFetchNameValue( papszOptions, "TEMPORARY" ) != NULL &&
377 3 : CSLTestBoolean(CSLFetchNameValue( papszOptions, "TEMPORARY" ));
378 3 : if (bTemporary)
379 : {
380 0 : CPLFree(pszSchemaName);
381 0 : pszSchemaName = CPLStrdup("pg_temp_1");
382 0 : osCreateTable.Printf("CREATE TEMPORARY TABLE \"%s\"", pszTableName);
383 : }
384 : else
385 3 : osCreateTable.Printf("CREATE TABLE \"%s\".\"%s\"", pszSchemaName, pszTableName);
386 :
387 3 : if( !bHavePostGIS )
388 : {
389 1 : if (eType == wkbNone)
390 : osCommand.Printf(
391 : "%s ( "
392 : " %s SERIAL, "
393 : " CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
394 1 : osCreateTable.c_str(), pszFIDColumn, pszTableName, pszFIDColumn );
395 : else
396 : osCommand.Printf(
397 : "%s ( "
398 : " %s SERIAL, "
399 : " WKB_GEOMETRY %s, "
400 : " CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
401 0 : osCreateTable.c_str(), pszFIDColumn, pszGeomType, pszTableName, pszFIDColumn );
402 : }
403 2 : else if ( EQUAL(pszGeomType, "geography") )
404 : {
405 0 : if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL )
406 0 : pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
407 : else
408 0 : pszGFldName = "the_geog";
409 :
410 0 : if (nSRSId)
411 : osCommand.Printf(
412 : "%s ( %s SERIAL, \"%s\" geography(%s%s,%d), CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
413 0 : osCreateTable.c_str(), pszFIDColumn, pszGFldName, pszGeometryType, nDimension == 2 ? "" : "Z", nSRSId, pszTableName, pszFIDColumn );
414 : else
415 : osCommand.Printf(
416 : "%s ( %s SERIAL, \"%s\" geography(%s%s), CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
417 0 : osCreateTable.c_str(), pszFIDColumn, pszGFldName, pszGeometryType, nDimension == 2 ? "" : "Z", pszTableName, pszFIDColumn );
418 : }
419 : else
420 : {
421 : osCommand.Printf(
422 : "%s ( %s SERIAL, CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
423 2 : osCreateTable.c_str(), pszFIDColumn, pszTableName, pszFIDColumn );
424 : }
425 :
426 3 : if (bCreateTable)
427 3 : Log(osCommand);
428 :
429 : /* -------------------------------------------------------------------- */
430 : /* Eventually we should be adding this table to a table of */
431 : /* "geometric layers", capturing the WKT projection, and */
432 : /* perhaps some other housekeeping. */
433 : /* -------------------------------------------------------------------- */
434 3 : if( bCreateTable && bHavePostGIS && !EQUAL(pszGeomType, "geography"))
435 : {
436 : osCommand.Printf(
437 : "SELECT AddGeometryColumn('%s','%s','%s',%d,'%s',%d)",
438 : pszSchemaName, pszTableName, pszGFldName,
439 2 : nSRSId, pszGeometryType, nDimension );
440 2 : Log(osCommand);
441 : }
442 :
443 3 : if( bCreateTable && bHavePostGIS )
444 : {
445 : /* -------------------------------------------------------------------- */
446 : /* Create the spatial index. */
447 : /* */
448 : /* We're doing this before we add geometry and record to the table */
449 : /* so this may not be exactly the best way to do it. */
450 : /* -------------------------------------------------------------------- */
451 2 : const char *pszSI = CSLFetchNameValue( papszOptions, "SPATIAL_INDEX" );
452 2 : if( pszSI == NULL || CSLTestBoolean(pszSI) )
453 : {
454 : osCommand.Printf("CREATE INDEX \"%s_geom_idx\" "
455 : "ON \"%s\".\"%s\" "
456 : "USING GIST (\"%s\")",
457 2 : pszTableName, pszSchemaName, pszTableName, pszGFldName);
458 :
459 2 : Log(osCommand);
460 : }
461 : }
462 :
463 : /* -------------------------------------------------------------------- */
464 : /* Create the layer object. */
465 : /* -------------------------------------------------------------------- */
466 : OGRPGDumpLayer *poLayer;
467 :
468 3 : int bWriteAsHex = !CSLFetchBoolean(papszOptions,"WRITE_EWKT_GEOM",FALSE);
469 :
470 : poLayer = new OGRPGDumpLayer( this, pszSchemaName, pszLayerNameIn, pszGFldName,
471 3 : pszFIDColumn, nDimension, nSRSId, bWriteAsHex, bCreateTable );
472 6 : poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) );
473 3 : poLayer->SetPrecisionFlag( CSLFetchBoolean(papszOptions,"PRECISION",TRUE));
474 :
475 : /* -------------------------------------------------------------------- */
476 : /* Add layer to data source layer list. */
477 : /* -------------------------------------------------------------------- */
478 : papoLayers = (OGRPGDumpLayer **)
479 3 : CPLRealloc( papoLayers, sizeof(OGRPGDumpLayer *) * (nLayers+1) );
480 :
481 3 : papoLayers[nLayers++] = poLayer;
482 :
483 3 : CPLFree( pszLayerName );
484 3 : CPLFree( pszSchemaName );
485 :
486 3 : return poLayer;
487 : }
488 :
489 : /************************************************************************/
490 : /* TestCapability() */
491 : /************************************************************************/
492 :
493 0 : int OGRPGDumpDataSource::TestCapability( const char * pszCap )
494 :
495 : {
496 0 : if( EQUAL(pszCap,ODsCCreateLayer) )
497 0 : return TRUE;
498 : else
499 0 : return FALSE;
500 : }
501 :
502 : /************************************************************************/
503 : /* GetLayer() */
504 : /************************************************************************/
505 :
506 0 : OGRLayer *OGRPGDumpDataSource::GetLayer( int iLayer )
507 :
508 : {
509 0 : if( iLayer < 0 || iLayer >= nLayers )
510 0 : return NULL;
511 : else
512 0 : return papoLayers[iLayer];
513 : }
514 :
515 : /************************************************************************/
516 : /* Log() */
517 : /************************************************************************/
518 :
519 68 : void OGRPGDumpDataSource::Log(const char* pszStr, int bAddSemiColumn)
520 : {
521 68 : if (fp == NULL)
522 : {
523 3 : if (bTriedOpen)
524 0 : return;
525 3 : bTriedOpen = TRUE;
526 3 : fp = VSIFOpenL(pszName, "wb");
527 3 : if (fp == NULL)
528 : {
529 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszName);
530 0 : return;
531 : }
532 : }
533 :
534 68 : if (bAddSemiColumn)
535 46 : VSIFPrintfL(fp, "%s;%s", pszStr, pszEOL);
536 : else
537 22 : VSIFPrintfL(fp, "%s%s", pszStr, pszEOL);
538 : }
539 :
540 : /************************************************************************/
541 : /* StartCopy() */
542 : /************************************************************************/
543 2 : void OGRPGDumpDataSource::StartCopy( OGRPGDumpLayer *poPGLayer )
544 : {
545 2 : EndCopy();
546 2 : poLayerInCopyMode = poPGLayer;
547 2 : }
548 :
549 : /************************************************************************/
550 : /* EndCopy() */
551 : /************************************************************************/
552 8 : OGRErr OGRPGDumpDataSource::EndCopy( )
553 : {
554 8 : if( poLayerInCopyMode != NULL )
555 : {
556 2 : OGRErr result = poLayerInCopyMode->EndCopy();
557 2 : poLayerInCopyMode = NULL;
558 :
559 2 : return result;
560 : }
561 : else
562 6 : return OGRERR_NONE;
563 : }
|