1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Implements OGRPGDumpDataSource class.
5  * Author:   Even Rouault, <even dot rouault at spatialys.com>
6  *
7  ******************************************************************************
8  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.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  *
new() -> Self17  * 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 <cstring>
30 #include "ogr_pgdump.h"
31 #include "cpl_conv.h"
32 #include "cpl_string.h"
33 
34 CPL_CVSID("$Id: ogrpgdumpdatasource.cpp 379fc8667418dc54e864d828ea35be513e69abc9 2021-04-03 21:58:21 +0200 Even Rouault $")
35 
36 /************************************************************************/
37 /*                      OGRPGDumpDataSource()                           */
38 /************************************************************************/
39 
40 OGRPGDumpDataSource::OGRPGDumpDataSource( const char* pszNameIn,
41                                           char** papszOptions ) :
42     pszName(CPLStrdup(pszNameIn))
43 {
44     const char *pszCRLFFormat = CSLFetchNameValue( papszOptions, "LINEFORMAT");
45 
46     bool bUseCRLF = false;
47     if( pszCRLFFormat == nullptr )
48     {
49 #ifdef WIN32
50         bUseCRLF = true;
51 #endif
52     }
53     else if( EQUAL(pszCRLFFormat, "CRLF") )
54     {
55         bUseCRLF = true;
56     }
57     else if( EQUAL(pszCRLFFormat, "LF") )
58     {
59         bUseCRLF = false;
60     }
61     else
62     {
63         CPLError( CE_Warning, CPLE_AppDefined,
64                   "LINEFORMAT=%s not understood, use one of CRLF or LF.",
65                   pszCRLFFormat );
66 #ifdef WIN32
67         bUseCRLF = true;
68 #endif
69     }
70 
71     if( bUseCRLF )
72         pszEOL =  "\r\n";
73 }
74 
75 /************************************************************************/
detail(&mut self, val: i32) -> &mut Self76 /*                          ~OGRPGDumpDataSource()                          */
77 /************************************************************************/
78 
79 OGRPGDumpDataSource::~OGRPGDumpDataSource()
80 
81 {
82     EndCopy();
83     for( int i = 0; i < nLayers; i++ )
84         delete papoLayers[i];
85 
86     if( fp )
87     {
88         LogCommit();
89         VSIFCloseL(fp);
90         fp = nullptr;
91     }
92     CPLFree(papoLayers);
93     CPLFree(pszName);
94 }
95 
96 /************************************************************************/
97 /*                         LogStartTransaction()                        */
98 /************************************************************************/
99 
100 void OGRPGDumpDataSource::LogStartTransaction()
101 {
102     if( bInTransaction )
103         return;
alt_key(&mut self, val: bool) -> &mut Self104     bInTransaction = true;
105     Log("BEGIN");
106 }
107 
108 /************************************************************************/
109 /*                             LogCommit()                              */
110 /************************************************************************/
111 
112 void OGRPGDumpDataSource::LogCommit()
113 {
114     EndCopy();
115 
116     if( !bInTransaction )
117         return;
ctrl_key(&mut self, val: bool) -> &mut Self118     bInTransaction = false;
119     Log("COMMIT");
120 }
121 
122 /************************************************************************/
123 /*                         OGRPGCommonLaunderName()                     */
124 /************************************************************************/
125 
126 char *OGRPGCommonLaunderName( const char *pszSrcName,
127                               const char* pszDebugPrefix )
128 
129 {
130     char *pszSafeName = CPLStrdup( pszSrcName );
131 
132     for( int i = 0; pszSafeName[i] != '\0'; i++ )
133     {
134         pszSafeName[i] = (char) tolower( pszSafeName[i] );
meta_key(&mut self, val: bool) -> &mut Self135         if( pszSafeName[i] == '\'' ||
136             pszSafeName[i] == '-' ||
137             pszSafeName[i] == '#' )
138         {
139             pszSafeName[i] = '_';
140         }
141     }
142 
143     if( strcmp(pszSrcName,pszSafeName) != 0 )
144         CPLDebug(pszDebugPrefix, "LaunderName('%s') -> '%s'",
145                  pszSrcName, pszSafeName);
146 
147     return pszSafeName;
148 }
149 
150 /************************************************************************/
151 /*                           ICreateLayer()                             */
modifier_alt_graph(&mut self, val: bool) -> &mut Self152 /************************************************************************/
153 
154 OGRLayer *
155 OGRPGDumpDataSource::ICreateLayer( const char * pszLayerName,
156                                    OGRSpatialReference *poSRS,
157                                    OGRwkbGeometryType eType,
158                                    char ** papszOptions )
159 
160 {
161     const char* pszFIDColumnNameIn = CSLFetchNameValue(papszOptions, "FID");
162     CPLString osFIDColumnName;
163     if (pszFIDColumnNameIn == nullptr)
164         osFIDColumnName = "ogc_fid";
165     else
166     {
167         if( CPLFetchBool(papszOptions,"LAUNDER", true) )
168         {
169             char *pszLaunderedFid =
170                 OGRPGCommonLaunderName(pszFIDColumnNameIn, "PGDump");
171             osFIDColumnName = pszLaunderedFid;
172             CPLFree(pszLaunderedFid);
173         }
174         else
175         {
176             osFIDColumnName = pszFIDColumnNameIn;
177         }
178     }
179     const CPLString osFIDColumnNameEscaped =
180         OGRPGDumpEscapeColumnName(osFIDColumnName);
181 
182     if (STARTS_WITH(pszLayerName, "pg"))
183     {
184         CPLError(CE_Warning, CPLE_AppDefined,
185                  "The layer name should not begin by 'pg' as it is a reserved "
186                  "prefix");
187     }
188 
189     bool bHavePostGIS = true;
190     // bHavePostGIS = CPLFetchBool(papszOptions, "POSTGIS", true);
191 
192     const bool bCreateTable = CPLFetchBool(papszOptions, "CREATE_TABLE", true);
193     const bool bCreateSchema =
194         CPLFetchBool(papszOptions, "CREATE_SCHEMA", true);
195     const char* pszDropTable =
196         CSLFetchNameValueDef(papszOptions, "DROP_TABLE", "IF_EXISTS");
197     int GeometryTypeFlags = 0;
198 
199     if( OGR_GT_HasZ((OGRwkbGeometryType)eType) )
200         GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
201     if( OGR_GT_HasM((OGRwkbGeometryType)eType) )
202         GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
203 
204     int ForcedGeometryTypeFlags = -1;
205     const char* pszDim = CSLFetchNameValue( papszOptions, "DIM");
206     if( pszDim != nullptr )
207     {
208         if( EQUAL(pszDim, "XY") || EQUAL(pszDim, "2") )
209         {
210             GeometryTypeFlags = 0;
211             ForcedGeometryTypeFlags = GeometryTypeFlags;
212         }
213         else if( EQUAL(pszDim, "XYZ") || EQUAL(pszDim, "3") )
214         {
215             GeometryTypeFlags = OGRGeometry::OGR_G_3D;
216             ForcedGeometryTypeFlags = GeometryTypeFlags;
217         }
218         else if( EQUAL(pszDim, "XYM") )
219         {
220             GeometryTypeFlags = OGRGeometry::OGR_G_MEASURED;
221             ForcedGeometryTypeFlags = GeometryTypeFlags;
222         }
223         else if( EQUAL(pszDim, "XYZM") || EQUAL(pszDim, "4") )
224         {
225             GeometryTypeFlags = OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
226             ForcedGeometryTypeFlags = GeometryTypeFlags;
227         }
228         else
229         {
230             CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for DIM");
231         }
232     }
233 
234     const int nDimension =
235         2 + ((GeometryTypeFlags & OGRGeometry::OGR_G_3D) ? 1 : 0)
236         + ((GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) ? 1 : 0);
237 
238     /* Should we turn layers with None geometry type as Unknown/GEOMETRY */
239     /* so they are still recorded in geometry_columns table ? (#4012) */
240     const bool bNoneAsUnknown =
241         CPLTestBool(
242             CSLFetchNameValueDef(papszOptions, "NONE_AS_UNKNOWN", "NO"));
243 
244     if( bNoneAsUnknown && eType == wkbNone )
245         eType = wkbUnknown;
246     else if( eType == wkbNone )
247         bHavePostGIS = false;
248 
249     const bool bExtractSchemaFromLayerName =
250         CPLTestBool(CSLFetchNameValueDef(
251             papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
252 
253     // Postgres Schema handling:
254 
255     // Extract schema name from input layer name or passed with -lco SCHEMA.
256     // Set layer name to "schema.table" or to "table" if schema ==
257     // current_schema() Usage without schema name is backwards compatible
258 
259     const char* pszDotPos = strstr(pszLayerName,".");
260     char *pszTableName = nullptr;
261     char *pszSchemaName = nullptr;
262 
263     if ( pszDotPos != nullptr && bExtractSchemaFromLayerName )
264     {
265       const int length = static_cast<int>(pszDotPos - pszLayerName);
266       pszSchemaName = (char*)CPLMalloc(length+1);
267       strncpy(pszSchemaName, pszLayerName, length);
268       pszSchemaName[length] = '\0';
269 
270       if( CPLFetchBool(papszOptions, "LAUNDER", true) )
271           pszTableName = OGRPGCommonLaunderName( pszDotPos + 1, "PGDump" ); //skip "."
272       else
273           pszTableName = CPLStrdup( pszDotPos + 1 ); //skip "."
274     }
275     else
276     {
277       pszSchemaName = nullptr;
278       if( CPLFetchBool(papszOptions, "LAUNDER", true) )
279           pszTableName = OGRPGCommonLaunderName( pszLayerName, "PGDump" ); //skip "."
280       else
281           pszTableName = CPLStrdup( pszLayerName ); //skip "."
282     }
283 
284     LogCommit();
285 
286 /* -------------------------------------------------------------------- */
287 /*      Set the default schema for the layers.                          */
288 /* -------------------------------------------------------------------- */
289     CPLString osCommand;
290 
291     if( CSLFetchNameValue( papszOptions, "SCHEMA" ) != nullptr )
292     {
293         CPLFree(pszSchemaName);
294         pszSchemaName = CPLStrdup(CSLFetchNameValue( papszOptions, "SCHEMA" ));
295         if( bCreateSchema )
296         {
297             osCommand.Printf("CREATE SCHEMA \"%s\"", pszSchemaName);
298             Log(osCommand);
299         }
300     }
301 
302     if ( pszSchemaName == nullptr)
303     {
304         pszSchemaName = CPLStrdup("public");
305     }
306 
307 /* -------------------------------------------------------------------- */
308 /*      Do we already have this layer?                                  */
309 /* -------------------------------------------------------------------- */
310     for( int iLayer = 0; iLayer < nLayers; iLayer++ )
311     {
312         if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) )
313         {
314             CPLError( CE_Failure, CPLE_AppDefined,
315                       "Layer %s already exists, CreateLayer failed.\n",
316                       pszLayerName );
317             CPLFree( pszTableName );
318             CPLFree( pszSchemaName );
319             return nullptr;
320         }
321     }
322 
323     if( bCreateTable && (EQUAL(pszDropTable, "YES") ||
324                          EQUAL(pszDropTable, "ON") ||
325                          EQUAL(pszDropTable, "TRUE") ||
326                          EQUAL(pszDropTable, "IF_EXISTS")) )
327     {
328         if (EQUAL(pszDropTable, "IF_EXISTS"))
329             osCommand.Printf("DROP TABLE IF EXISTS \"%s\".\"%s\" CASCADE",
330                              pszSchemaName, pszTableName );
331         else
332             osCommand.Printf("DROP TABLE \"%s\".\"%s\" CASCADE",
333                              pszSchemaName, pszTableName );
334         Log(osCommand);
335     }
336 
337 /* -------------------------------------------------------------------- */
338 /*      Handle the GEOM_TYPE option.                                    */
339 /* -------------------------------------------------------------------- */
340     const char *pszGeomType = CSLFetchNameValue( papszOptions, "GEOM_TYPE" );
341     if( pszGeomType == nullptr )
342     {
343         pszGeomType = "geometry";
344     }
345 
346     if( !EQUAL(pszGeomType,"geometry") && !EQUAL(pszGeomType, "geography"))
347     {
348         CPLError(
349             CE_Failure, CPLE_AppDefined,
350             "GEOM_TYPE in PostGIS enabled databases must be 'geometry' or "
351             "'geography'.  Creation of layer %s with GEOM_TYPE %s has failed.",
352             pszLayerName, pszGeomType );
353         CPLFree( pszTableName );
354         CPLFree( pszSchemaName );
355         return nullptr;
356     }
357 
358 /* -------------------------------------------------------------------- */
359 /*      Try to get the SRS Id of this spatial reference system,         */
360 /*      adding tot the srs table if needed.                             */
361 /* -------------------------------------------------------------------- */
362     const char* pszPostgisVersion =
363         CSLFetchNameValueDef( papszOptions, "POSTGIS_VERSION", "2.2" );
364     const int nPostGISMajor = atoi(pszPostgisVersion);
365     const char* pszPostgisVersionDot = strchr(pszPostgisVersion, '.');
366     const int nPostGISMinor =
367           pszPostgisVersionDot ? atoi(pszPostgisVersionDot+1) : 0;
368     const int nUnknownSRSId = nPostGISMajor >= 2 ? 0 : -1;
369 
370     int nSRSId = nUnknownSRSId;
371     int nForcedSRSId = -2;
372     if( CSLFetchNameValue( papszOptions, "SRID") != nullptr )
373     {
374         nSRSId = atoi(CSLFetchNameValue( papszOptions, "SRID"));
375         nForcedSRSId = nSRSId;
376     }
377     else
378     {
379         if( poSRS )
380         {
381             const char* pszAuthorityName = poSRS->GetAuthorityName(nullptr);
382             if( pszAuthorityName != nullptr && EQUAL( pszAuthorityName, "EPSG" ) )
383             {
384                 /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */
385                 nSRSId = atoi( poSRS->GetAuthorityCode(nullptr) );
386             }
387             else
388             {
389                 const char* pszGeogCSName = poSRS->GetAttrValue("GEOGCS");
390                 if( pszGeogCSName != nullptr &&
391                     EQUAL(pszGeogCSName, "GCS_WGS_1984") )
392                 {
393                     nSRSId = 4326;
394                 }
395             }
396         }
397     }
398 
399     CPLString osEscapedTableNameSingleQuote =
400         OGRPGDumpEscapeString(pszTableName);
401     const char* pszEscapedTableNameSingleQuote =
402         osEscapedTableNameSingleQuote.c_str();
403 
404     const char *pszGeometryType = OGRToOGCGeomType(eType);
405 
406     const char *pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
407     if( bHavePostGIS && !EQUAL(pszGeomType, "geography") )
408     {
409         if( pszGFldName == nullptr )
410             pszGFldName = "wkb_geometry";
411 
412         if( nPostGISMajor < 2 )
413         {
414             // Sometimes there is an old cruft entry in the geometry_columns
415             // table if things were not properly cleaned up before.  We make
416             // an effort to clean out such cruft.
417             //
418             // Note: PostGIS 2.0 defines geometry_columns as a view (no clean up
419             // is needed).
420 
421             osCommand.Printf(
422                 "DELETE FROM geometry_columns "
423                 "WHERE f_table_name = %s AND f_table_schema = '%s'",
424                 pszEscapedTableNameSingleQuote, pszSchemaName );
425             if( bCreateTable )
426                 Log(osCommand);
427         }
428     }
429 
430     LogStartTransaction();
431 
432 /* -------------------------------------------------------------------- */
433 /*      Create a basic table with the FID.  Also include the            */
434 /*      geometry if this is not a PostGIS enabled table.                */
435 /* -------------------------------------------------------------------- */
436     const bool bFID64 = CPLFetchBool(papszOptions, "FID64", false);
437     const char* pszSerialType = bFID64 ? "BIGSERIAL": "SERIAL";
438 
439     CPLString osCreateTable;
440     const bool bTemporary = CPLFetchBool( papszOptions, "TEMPORARY", false );
441     if( bTemporary )
442     {
443         CPLFree(pszSchemaName);
444         pszSchemaName = CPLStrdup("pg_temp_1");
445         osCreateTable.Printf("CREATE TEMPORARY TABLE \"%s\"", pszTableName);
446     }
447     else
448     {
449         osCreateTable.Printf("CREATE%s TABLE \"%s\".\"%s\"",
450                              CPLFetchBool( papszOptions, "UNLOGGED", false ) ?
451                              " UNLOGGED": "",
452                              pszSchemaName, pszTableName);
453     }
454 
455     if( !bHavePostGIS )
456     {
457         if (eType == wkbNone)
458             osCommand.Printf(
459                 "%s ( "
460                 "   %s %s, "
461                 "   CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
462                 osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
463                 pszSerialType, pszTableName, osFIDColumnNameEscaped.c_str() );
464         else
465             osCommand.Printf(
466                 "%s ( "
467                 "   %s %s, "
468                 "   WKB_GEOMETRY %s, "
469                 "   CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
470                 osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
471                 pszSerialType, pszGeomType, pszTableName,
472                 osFIDColumnNameEscaped.c_str() );
473     }
474     else if ( EQUAL(pszGeomType, "geography") )
475     {
476         if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != nullptr )
477             pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
478         else
479             pszGFldName = "the_geog";
480 
481         const char *suffix = "";
482         if( (GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) &&
483             (GeometryTypeFlags & OGRGeometry::OGR_G_3D) )
484         {
485             suffix = "ZM";
486         }
487         else if( (GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) )
488         {
489             suffix = "M";
490         }
491         else if( (GeometryTypeFlags & OGRGeometry::OGR_G_3D) )
492         {
493             suffix = "Z";
494         }
495 
496         if( nSRSId )
497             osCommand.Printf(
498                 "%s ( %s %s, \"%s\" geography(%s%s,%d), "
499                 "CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
500                 osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
501                 pszSerialType, pszGFldName, pszGeometryType, suffix, nSRSId,
502                 pszTableName, osFIDColumnNameEscaped.c_str() );
503         else
504             osCommand.Printf(
505                 "%s ( %s %s, \"%s\" geography(%s%s), "
506                 "CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
507                 osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
508                 pszSerialType, pszGFldName, pszGeometryType, suffix,
509                 pszTableName, osFIDColumnNameEscaped.c_str() );
510     }
511     else
512     {
513         osCommand.Printf(
514             "%s ( %s %s, CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
515             osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
516             pszSerialType, pszTableName, osFIDColumnNameEscaped.c_str() );
517     }
518 
519     if( bCreateTable )
520         Log(osCommand);
521 
522 /* -------------------------------------------------------------------- */
523 /*      Eventually we should be adding this table to a table of         */
524 /*      "geometric layers", capturing the WKT projection, and           */
525 /*      perhaps some other housekeeping.                                */
526 /* -------------------------------------------------------------------- */
527     if( bCreateTable && bHavePostGIS && !EQUAL(pszGeomType, "geography") )
528     {
529         const char *suffix = "";
530         if( GeometryTypeFlags == static_cast<int>(OGRGeometry::OGR_G_MEASURED) &&
531             wkbFlatten(eType) != wkbUnknown )
532         {
533             suffix = "M";
534         }
535 
536         osCommand.Printf(
537                 "SELECT AddGeometryColumn('%s',%s,'%s',%d,'%s%s',%d)",
538                 pszSchemaName, pszEscapedTableNameSingleQuote, pszGFldName,
539                 nSRSId, pszGeometryType, suffix, nDimension );
540         Log(osCommand);
541     }
542 
543     const char *pszSI = CSLFetchNameValueDef( papszOptions, "SPATIAL_INDEX", "GIST" );
544     const bool bCreateSpatialIndex = ( EQUAL(pszSI, "GIST") ||
545         EQUAL(pszSI, "SPGIST") || EQUAL(pszSI, "BRIN") ||
546         EQUAL(pszSI, "YES") || EQUAL(pszSI, "ON") || EQUAL(pszSI, "TRUE") );
547     if( !bCreateSpatialIndex && !EQUAL(pszSI, "NO") && !EQUAL(pszSI, "OFF") &&
548         !EQUAL(pszSI, "FALSE") && !EQUAL(pszSI, "NONE") )
549     {
550         CPLError(CE_Warning, CPLE_NotSupported,
551                  "SPATIAL_INDEX=%s not supported", pszSI);
552     }
553     const char* pszSpatialIndexType = EQUAL(pszSI, "SPGIST") ? "SPGIST" :
554                                       EQUAL(pszSI, "BRIN") ? "BRIN" : "GIST";
555 
556     if( bCreateTable && bHavePostGIS && bCreateSpatialIndex )
557     {
558 /* -------------------------------------------------------------------- */
559 /*      Create the spatial index.                                       */
560 /*                                                                      */
561 /*      We're doing this before we add geometry and record to the table */
562 /*      so this may not be exactly the best way to do it.               */
563 /* -------------------------------------------------------------------- */
564         osCommand.Printf(
565             "CREATE INDEX \"%s_%s_geom_idx\" "
566             "ON \"%s\".\"%s\" "
567             "USING %s (\"%s\")",
568             pszTableName, pszGFldName, pszSchemaName, pszTableName,
569             pszSpatialIndexType,
570             pszGFldName);
571 
572         Log(osCommand);
573     }
574 
575 /* -------------------------------------------------------------------- */
576 /*      Create the layer object.                                        */
577 /* -------------------------------------------------------------------- */
578     const bool bWriteAsHex =
579         !CPLFetchBool(papszOptions, "WRITE_EWKT_GEOM", false);
580 
581     OGRPGDumpLayer *poLayer =
582         new OGRPGDumpLayer( this, pszSchemaName, pszTableName,
583                             osFIDColumnName, bWriteAsHex, bCreateTable );
584     poLayer->SetLaunderFlag( CPLFetchBool(papszOptions, "LAUNDER", true) );
585     poLayer->SetPrecisionFlag( CPLFetchBool(papszOptions, "PRECISION", true));
586 
587     const char* pszOverrideColumnTypes =
588         CSLFetchNameValue( papszOptions, "COLUMN_TYPES" );
589     poLayer->SetOverrideColumnTypes(pszOverrideColumnTypes);
590     poLayer->SetUnknownSRSId(nUnknownSRSId);
591     poLayer->SetForcedSRSId(nForcedSRSId);
592     poLayer->SetCreateSpatialIndex(bCreateSpatialIndex, pszSpatialIndexType);
593     poLayer->SetPostGISVersion(nPostGISMajor, nPostGISMinor);
594     poLayer->SetForcedGeometryTypeFlags(ForcedGeometryTypeFlags);
595 
596     const char* pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
597     if( pszDescription != nullptr )
598         poLayer->SetForcedDescription( pszDescription );
599 
600     if( bHavePostGIS )
601     {
602         OGRGeomFieldDefn oTmp( pszGFldName, eType );
603         OGRPGDumpGeomFieldDefn *poGeomField =
604             new OGRPGDumpGeomFieldDefn(&oTmp);
605         poGeomField->nSRSId = nSRSId;
606         poGeomField->GeometryTypeFlags = GeometryTypeFlags;
607         poLayer->GetLayerDefn()->AddGeomFieldDefn(poGeomField, FALSE);
608     }
609     else if( pszGFldName )
610         poLayer->SetGeometryFieldName(pszGFldName);
611 
612 /* -------------------------------------------------------------------- */
613 /*      Add layer to data source layer list.                            */
614 /* -------------------------------------------------------------------- */
615     papoLayers = (OGRPGDumpLayer **)
616         CPLRealloc( papoLayers,  sizeof(OGRPGDumpLayer *) * (nLayers+1) );
617 
618     papoLayers[nLayers++] = poLayer;
619 
620     CPLFree( pszTableName );
621     CPLFree( pszSchemaName );
622 
623     return poLayer;
624 }
625 
626 /************************************************************************/
627 /*                           TestCapability()                           */
628 /************************************************************************/
629 
630 int OGRPGDumpDataSource::TestCapability( const char * pszCap )
631 
632 {
633     if( EQUAL(pszCap,ODsCCreateLayer) )
634         return TRUE;
635     else if( EQUAL(pszCap,ODsCCreateGeomFieldAfterCreateLayer) )
636         return TRUE;
637     else if( EQUAL(pszCap,ODsCCurveGeometries) )
638         return TRUE;
639     else if( EQUAL(pszCap,ODsCMeasuredGeometries) )
640         return TRUE;
641     else if( EQUAL(pszCap,ODsCRandomLayerWrite) )
642         return TRUE;
643     else
644         return FALSE;
645 }
646 
647 /************************************************************************/
648 /*                              GetLayer()                              */
649 /************************************************************************/
650 
651 OGRLayer *OGRPGDumpDataSource::GetLayer( int iLayer )
652 
653 {
654     if( iLayer < 0 || iLayer >= nLayers )
655         return nullptr;
656     else
657         return papoLayers[iLayer];
658 }
659 
660 /************************************************************************/
661 /*                                  Log()                               */
662 /************************************************************************/
663 
664 bool OGRPGDumpDataSource::Log( const char* pszStr, bool bAddSemiColumn )
665 {
666     if( fp == nullptr )
667     {
668         if( bTriedOpen )
669             return false;
670         bTriedOpen = true;
671         fp = VSIFOpenL(pszName, "wb");
672         if (fp == nullptr)
673         {
674             CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszName);
675             return false;
676         }
677     }
678 
679     if( bAddSemiColumn )
680         VSIFPrintfL(fp, "%s;%s", pszStr, pszEOL);
681     else
682         VSIFPrintfL(fp, "%s%s", pszStr, pszEOL);
683     return true;
684 }
685 
686 /************************************************************************/
687 /*                             StartCopy()                              */
688 /************************************************************************/
689 void OGRPGDumpDataSource::StartCopy( OGRPGDumpLayer *poPGLayer )
690 {
691     EndCopy();
692     poLayerInCopyMode = poPGLayer;
693 }
694 
695 /************************************************************************/
696 /*                              EndCopy()                               */
697 /************************************************************************/
698 OGRErr OGRPGDumpDataSource::EndCopy()
699 {
700     if( poLayerInCopyMode != nullptr )
701     {
702         OGRErr result = poLayerInCopyMode->EndCopy();
703         poLayerInCopyMode = nullptr;
704 
705         return result;
706     }
707 
708     return OGRERR_NONE;
709 }
710