Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumpdatasource.cpp
Line
Count
Source
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
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include <algorithm>
14
#include <cstring>
15
#include "ogr_pgdump.h"
16
#include "cpl_conv.h"
17
#include "cpl_md5.h"
18
#include "cpl_string.h"
19
20
/************************************************************************/
21
/*                        OGRPGDumpDataSource()                         */
22
/************************************************************************/
23
24
OGRPGDumpDataSource::OGRPGDumpDataSource(const char *pszNameIn,
25
                                         CSLConstList papszOptions)
26
278
{
27
278
    SetDescription(pszNameIn);
28
29
278
    const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
30
31
278
    bool bUseCRLF = false;
32
278
    if (pszCRLFFormat == nullptr)
33
278
    {
34
#ifdef _WIN32
35
        bUseCRLF = true;
36
#endif
37
278
    }
38
0
    else if (EQUAL(pszCRLFFormat, "CRLF"))
39
0
    {
40
0
        bUseCRLF = true;
41
0
    }
42
0
    else if (EQUAL(pszCRLFFormat, "LF"))
43
0
    {
44
0
        bUseCRLF = false;
45
0
    }
46
0
    else
47
0
    {
48
0
        CPLError(CE_Warning, CPLE_AppDefined,
49
0
                 "LINEFORMAT=%s not understood, use one of CRLF or LF.",
50
0
                 pszCRLFFormat);
51
#ifdef _WIN32
52
        bUseCRLF = true;
53
#endif
54
0
    }
55
56
278
    if (bUseCRLF)
57
0
        m_pszEOL = "\r\n";
58
59
278
    m_fp = VSIFOpenL(pszNameIn, "wb");
60
278
    if (m_fp == nullptr)
61
0
    {
62
0
        CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszNameIn);
63
0
        return;
64
0
    }
65
278
}
66
67
/************************************************************************/
68
/*                        ~OGRPGDumpDataSource()                        */
69
/************************************************************************/
70
71
OGRPGDumpDataSource::~OGRPGDumpDataSource()
72
73
278
{
74
278
    EndCopy();
75
278
    m_apoLayers.clear();
76
77
278
    if (m_fp)
78
278
    {
79
278
        LogCommit();
80
278
        VSIFCloseL(m_fp);
81
278
        m_fp = nullptr;
82
278
    }
83
278
}
84
85
/************************************************************************/
86
/*                        LogStartTransaction()                         */
87
/************************************************************************/
88
89
void OGRPGDumpDataSource::LogStartTransaction()
90
1.06k
{
91
1.06k
    if (m_bInTransaction)
92
0
        return;
93
1.06k
    m_bInTransaction = true;
94
1.06k
    Log("BEGIN");
95
1.06k
}
96
97
/************************************************************************/
98
/*                             LogCommit()                              */
99
/************************************************************************/
100
101
void OGRPGDumpDataSource::LogCommit()
102
1.34k
{
103
1.34k
    EndCopy();
104
105
1.34k
    if (!m_bInTransaction)
106
278
        return;
107
1.06k
    m_bInTransaction = false;
108
1.06k
    Log("COMMIT");
109
1.06k
}
110
111
/************************************************************************/
112
/*                       OGRPGCommonLaunderName()                       */
113
/************************************************************************/
114
115
char *OGRPGCommonLaunderName(const char *pszSrcName, const char *pszDebugPrefix,
116
                             bool bUTF8ToASCII)
117
118
39.0k
{
119
39.0k
    char *pszSafeName = bUTF8ToASCII ? CPLUTF8ForceToASCII(pszSrcName, '_')
120
39.0k
                                     : CPLStrdup(pszSrcName);
121
122
39.0k
    size_t i = 0;  // needed after loop
123
39.0k
    int iUTF8Char = 0;
124
574k
    for (; pszSafeName[i] != '\0'; i++)
125
536k
    {
126
536k
        if (static_cast<unsigned char>(pszSafeName[i]) <= 127)
127
474k
        {
128
474k
            pszSafeName[i] = static_cast<char>(
129
474k
                CPLTolower(static_cast<unsigned char>(pszSafeName[i])));
130
474k
            if (pszSafeName[i] == '\'' || pszSafeName[i] == '-' ||
131
474k
                pszSafeName[i] == '#')
132
1.12k
            {
133
1.12k
                pszSafeName[i] = '_';
134
1.12k
            }
135
474k
        }
136
137
        // Truncate string by making sure we don't cut in the
138
        // middle of a UTF-8 multibyte character
139
        // Continuation bytes of such characters are of the form
140
        // 10xxxxxx (0x80), whereas single-byte are 0xxxxxxx
141
        // and the start of a multi-byte is 11xxxxxx
142
536k
        if ((pszSafeName[i] & 0xc0) != 0x80)
143
521k
        {
144
521k
            ++iUTF8Char;
145
521k
            if (iUTF8Char == OGR_PG_NAMEDATALEN)
146
1.03k
                break;
147
521k
        }
148
536k
    }
149
150
39.0k
    if (iUTF8Char == OGR_PG_NAMEDATALEN && pszSafeName[i] != '\0')
151
1.03k
    {
152
1.03k
        constexpr int FIRST_8_CHARS_OF_MD5 = 8;
153
1.03k
        iUTF8Char = 0;
154
62.9k
        for (i = 0; pszSafeName[i]; ++i)
155
62.9k
        {
156
62.9k
            if ((pszSafeName[i] & 0xc0) != 0x80)
157
56.8k
            {
158
56.8k
                ++iUTF8Char;
159
56.8k
                if (iUTF8Char == OGR_PG_NAMEDATALEN - FIRST_8_CHARS_OF_MD5 - 1)
160
1.03k
                    break;
161
56.8k
            }
162
62.9k
        }
163
1.03k
        pszSafeName[i] = '_';
164
1.03k
        memcpy(pszSafeName + i + 1, CPLMD5String(pszSrcName),
165
1.03k
               FIRST_8_CHARS_OF_MD5);
166
1.03k
        i += FIRST_8_CHARS_OF_MD5 + 1;
167
1.03k
    }
168
169
39.0k
    pszSafeName[i] = '\0';
170
171
39.0k
    if (strcmp(pszSrcName, pszSafeName) != 0)
172
12.1k
    {
173
12.1k
        if (CPLStrlenUTF8Ex(pszSafeName) < CPLStrlenUTF8Ex(pszSrcName))
174
1.03k
        {
175
1.03k
            CPLError(CE_Warning, CPLE_AppDefined,
176
1.03k
                     "%s identifier truncated to %s", pszSrcName, pszSafeName);
177
1.03k
        }
178
11.0k
        else
179
11.0k
        {
180
11.0k
            CPLDebug(pszDebugPrefix, "LaunderName('%s') -> '%s'", pszSrcName,
181
11.0k
                     pszSafeName);
182
11.0k
        }
183
12.1k
    }
184
185
39.0k
    return pszSafeName;
186
39.0k
}
187
188
/************************************************************************/
189
/*                            ICreateLayer()                            */
190
/************************************************************************/
191
192
OGRLayer *
193
OGRPGDumpDataSource::ICreateLayer(const char *pszLayerName,
194
                                  const OGRGeomFieldDefn *poGeomFieldDefn,
195
                                  CSLConstList papszOptions)
196
197
1.06k
{
198
1.06k
    if (STARTS_WITH(pszLayerName, "pg"))
199
0
    {
200
0
        CPLError(CE_Warning, CPLE_AppDefined,
201
0
                 "The layer name should not begin by 'pg' as it is a reserved "
202
0
                 "prefix");
203
0
    }
204
205
1.06k
    auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
206
1.06k
    const auto poSRS =
207
1.06k
        poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
208
209
1.06k
    const bool bCreateTable = CPLFetchBool(papszOptions, "CREATE_TABLE", true);
210
1.06k
    const bool bCreateSchema =
211
1.06k
        CPLFetchBool(papszOptions, "CREATE_SCHEMA", true);
212
1.06k
    const char *pszDropTable =
213
1.06k
        CSLFetchNameValueDef(papszOptions, "DROP_TABLE", "IF_EXISTS");
214
1.06k
    const bool bSkipConflicts =
215
1.06k
        CPLFetchBool(papszOptions, "SKIP_CONFLICTS", false);
216
1.06k
    int nGeometryTypeFlags = 0;
217
218
1.06k
    if (OGR_GT_HasZ(eType))
219
6
        nGeometryTypeFlags |= OGRGeometry::OGR_G_3D;
220
1.06k
    if (OGR_GT_HasM(eType))
221
0
        nGeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
222
223
1.06k
    int nForcedGeometryTypeFlags = -1;
224
1.06k
    const char *pszDim = CSLFetchNameValue(papszOptions, "DIM");
225
1.06k
    if (pszDim != nullptr)
226
0
    {
227
0
        if (EQUAL(pszDim, "XY") || EQUAL(pszDim, "2"))
228
0
        {
229
0
            nGeometryTypeFlags = 0;
230
0
            nForcedGeometryTypeFlags = nGeometryTypeFlags;
231
0
        }
232
0
        else if (EQUAL(pszDim, "XYZ") || EQUAL(pszDim, "3"))
233
0
        {
234
0
            nGeometryTypeFlags = OGRGeometry::OGR_G_3D;
235
0
            nForcedGeometryTypeFlags = nGeometryTypeFlags;
236
0
        }
237
0
        else if (EQUAL(pszDim, "XYM"))
238
0
        {
239
0
            nGeometryTypeFlags = OGRGeometry::OGR_G_MEASURED;
240
0
            nForcedGeometryTypeFlags = nGeometryTypeFlags;
241
0
        }
242
0
        else if (EQUAL(pszDim, "XYZM") || EQUAL(pszDim, "4"))
243
0
        {
244
0
            nGeometryTypeFlags =
245
0
                OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
246
0
            nForcedGeometryTypeFlags = nGeometryTypeFlags;
247
0
        }
248
0
        else
249
0
        {
250
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for DIM");
251
0
        }
252
0
    }
253
254
1.06k
    const int nDimension =
255
1.06k
        2 + ((nGeometryTypeFlags & OGRGeometry::OGR_G_3D) ? 1 : 0) +
256
1.06k
        ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) ? 1 : 0);
257
258
    /* Should we turn layers with None geometry type as Unknown/GEOMETRY */
259
    /* so they are still recorded in geometry_columns table ? (#4012) */
260
1.06k
    const bool bNoneAsUnknown = CPLTestBool(
261
1.06k
        CSLFetchNameValueDef(papszOptions, "NONE_AS_UNKNOWN", "NO"));
262
263
1.06k
    if (bNoneAsUnknown && eType == wkbNone)
264
0
        eType = wkbUnknown;
265
266
1.06k
    const bool bExtractSchemaFromLayerName = CPLTestBool(CSLFetchNameValueDef(
267
1.06k
        papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
268
269
    // Postgres Schema handling:
270
271
    // Extract schema name from input layer name or passed with -lco SCHEMA.
272
    // Set layer name to "schema.table" or to "table" if schema ==
273
    // current_schema() Usage without schema name is backwards compatible
274
275
1.06k
    const char *pszDotPos = strstr(pszLayerName, ".");
276
1.06k
    std::string osTable;
277
1.06k
    std::string osSchema;
278
1.06k
    const bool bUTF8ToASCII =
279
1.06k
        CPLFetchBool(papszOptions, "LAUNDER_ASCII", false);
280
1.06k
    const bool bLaunder =
281
1.06k
        bUTF8ToASCII || CPLFetchBool(papszOptions, "LAUNDER", true);
282
283
1.06k
    if (pszDotPos != nullptr && bExtractSchemaFromLayerName)
284
87
    {
285
87
        const size_t length = static_cast<size_t>(pszDotPos - pszLayerName);
286
87
        osSchema = pszLayerName;
287
87
        osSchema.resize(length);
288
289
87
        if (bLaunder)
290
87
        {
291
87
            char *pszTmp = OGRPGCommonLaunderName(pszDotPos + 1, "PGDump",
292
87
                                                  bUTF8ToASCII);  // skip "."
293
87
            osTable = pszTmp;
294
87
            CPLFree(pszTmp);
295
87
        }
296
0
        else
297
0
            osTable = OGRPGCommonGenerateShortEnoughIdentifier(pszDotPos +
298
0
                                                               1);  // skip "."
299
87
    }
300
977
    else
301
977
    {
302
977
        if (bLaunder)
303
977
        {
304
977
            char *pszTmp =
305
977
                OGRPGCommonLaunderName(pszLayerName, "PGDump", bUTF8ToASCII);
306
977
            osTable = pszTmp;
307
977
            CPLFree(pszTmp);
308
977
        }
309
0
        else
310
0
            osTable = OGRPGCommonGenerateShortEnoughIdentifier(pszLayerName);
311
977
    }
312
313
1.06k
    const std::string osTableEscaped =
314
1.06k
        OGRPGDumpEscapeColumnName(osTable.c_str());
315
1.06k
    const char *pszTableEscaped = osTableEscaped.c_str();
316
317
1.06k
    LogCommit();
318
319
    /* -------------------------------------------------------------------- */
320
    /*      Set the default schema for the layers.                          */
321
    /* -------------------------------------------------------------------- */
322
1.06k
    CPLString osCommand;
323
324
1.06k
    const char *pszSchemaOption = CSLFetchNameValue(papszOptions, "SCHEMA");
325
1.06k
    if (pszSchemaOption)
326
0
    {
327
0
        osSchema = pszSchemaOption;
328
0
        if (bCreateSchema)
329
0
        {
330
0
            osCommand.Printf(
331
0
                "CREATE SCHEMA %s",
332
0
                OGRPGDumpEscapeColumnName(osSchema.c_str()).c_str());
333
0
            Log(osCommand);
334
0
        }
335
0
    }
336
337
1.06k
    const bool bTemporary = CPLFetchBool(papszOptions, "TEMPORARY", false);
338
1.06k
    if (bTemporary)
339
0
    {
340
0
        osSchema = "pg_temp";
341
0
    }
342
343
1.06k
    if (osSchema.empty())
344
978
    {
345
978
        osSchema = "public";
346
978
    }
347
1.06k
    const std::string osSchemaEscaped =
348
1.06k
        OGRPGDumpEscapeColumnName(osSchema.c_str());
349
1.06k
    const char *pszSchemaEscaped = osSchemaEscaped.c_str();
350
351
    /* -------------------------------------------------------------------- */
352
    /*      Do we already have this layer?                                  */
353
    /* -------------------------------------------------------------------- */
354
1.06k
    for (const auto &poLayer : m_apoLayers)
355
6.65k
    {
356
6.65k
        if (EQUAL(pszLayerName, poLayer->GetDescription()))
357
0
        {
358
0
            CPLError(CE_Failure, CPLE_AppDefined,
359
0
                     "Layer %s already exists, CreateLayer failed.\n",
360
0
                     pszLayerName);
361
0
            return nullptr;
362
0
        }
363
6.65k
    }
364
365
1.06k
    if (bCreateTable &&
366
1.06k
        (EQUAL(pszDropTable, "YES") || EQUAL(pszDropTable, "ON") ||
367
1.06k
         EQUAL(pszDropTable, "TRUE") || EQUAL(pszDropTable, "IF_EXISTS")))
368
1.06k
    {
369
1.06k
        if (EQUAL(pszDropTable, "IF_EXISTS"))
370
1.06k
            osCommand.Printf("DROP TABLE IF EXISTS %s.%s CASCADE",
371
1.06k
                             pszSchemaEscaped, pszTableEscaped);
372
0
        else
373
0
            osCommand.Printf("DROP TABLE %s.%s CASCADE", pszSchemaEscaped,
374
0
                             pszTableEscaped);
375
1.06k
        Log(osCommand);
376
1.06k
    }
377
378
    /* -------------------------------------------------------------------- */
379
    /*      Handle the GEOM_TYPE option.                                    */
380
    /* -------------------------------------------------------------------- */
381
1.06k
    const char *pszGeomType = CSLFetchNameValue(papszOptions, "GEOM_TYPE");
382
1.06k
    if (pszGeomType == nullptr)
383
1.06k
    {
384
1.06k
        pszGeomType = "geometry";
385
1.06k
    }
386
387
1.06k
    if (!EQUAL(pszGeomType, "geometry") && !EQUAL(pszGeomType, "geography"))
388
0
    {
389
0
        CPLError(
390
0
            CE_Failure, CPLE_AppDefined,
391
0
            "GEOM_TYPE in PostGIS enabled databases must be 'geometry' or "
392
0
            "'geography'.  Creation of layer %s with GEOM_TYPE %s has failed.",
393
0
            pszLayerName, pszGeomType);
394
0
        return nullptr;
395
0
    }
396
397
    /* -------------------------------------------------------------------- */
398
    /*      Try to get the SRS Id of this spatial reference system,         */
399
    /*      adding tot the srs table if needed.                             */
400
    /* -------------------------------------------------------------------- */
401
1.06k
    const char *pszPostgisVersion =
402
1.06k
        CSLFetchNameValueDef(papszOptions, "POSTGIS_VERSION", "2.2");
403
1.06k
    const int nPostGISMajor = atoi(pszPostgisVersion);
404
1.06k
    const char *pszPostgisVersionDot = strchr(pszPostgisVersion, '.');
405
1.06k
    const int nPostGISMinor =
406
1.06k
        pszPostgisVersionDot ? atoi(pszPostgisVersionDot + 1) : 0;
407
1.06k
    const int nUnknownSRSId = nPostGISMajor >= 2 ? 0 : -1;
408
409
1.06k
    int nSRSId = nUnknownSRSId;
410
1.06k
    int nForcedSRSId = -2;
411
1.06k
    const char *pszSRID = CSLFetchNameValue(papszOptions, "SRID");
412
1.06k
    if (pszSRID)
413
0
    {
414
0
        nSRSId = atoi(pszSRID);
415
0
        nForcedSRSId = nSRSId;
416
0
    }
417
1.06k
    else
418
1.06k
    {
419
1.06k
        if (poSRS)
420
16
        {
421
16
            const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr);
422
16
            if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
423
1
            {
424
                /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */
425
1
                nSRSId = atoi(poSRS->GetAuthorityCode(nullptr));
426
1
            }
427
15
            else
428
15
            {
429
15
                const char *pszGeogCSName = poSRS->GetAttrValue("GEOGCS");
430
15
                if (pszGeogCSName != nullptr &&
431
4
                    EQUAL(pszGeogCSName, "GCS_WGS_1984"))
432
0
                {
433
0
                    nSRSId = 4326;
434
0
                }
435
15
            }
436
16
        }
437
1.06k
    }
438
439
1.06k
    const std::string osEscapedTableNameSingleQuote =
440
1.06k
        OGRPGDumpEscapeString(osTable.c_str());
441
1.06k
    const char *pszEscapedTableNameSingleQuote =
442
1.06k
        osEscapedTableNameSingleQuote.c_str();
443
444
1.06k
    const char *pszGeometryType = OGRToOGCGeomType(eType);
445
446
1.06k
    const char *pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
447
1.06k
    if (eType != wkbNone && !EQUAL(pszGeomType, "geography"))
448
188
    {
449
188
        if (pszGFldName == nullptr)
450
188
            pszGFldName = "wkb_geometry";
451
452
188
        if (nPostGISMajor < 2)
453
0
        {
454
            // Sometimes there is an old cruft entry in the geometry_columns
455
            // table if things were not properly cleaned up before.  We make
456
            // an effort to clean out such cruft.
457
            //
458
            // Note: PostGIS 2.0 defines geometry_columns as a view (no clean up
459
            // is needed).
460
461
0
            osCommand.Printf("DELETE FROM geometry_columns "
462
0
                             "WHERE f_table_name = %s AND f_table_schema = %s",
463
0
                             pszEscapedTableNameSingleQuote,
464
0
                             OGRPGDumpEscapeString(osSchema.c_str()).c_str());
465
0
            if (bCreateTable)
466
0
                Log(osCommand);
467
0
        }
468
188
    }
469
470
1.06k
    LogStartTransaction();
471
472
    /* -------------------------------------------------------------------- */
473
    /*      Create an empty table first.                                    */
474
    /* -------------------------------------------------------------------- */
475
1.06k
    if (bCreateTable)
476
1.06k
    {
477
1.06k
        if (bTemporary)
478
0
        {
479
0
            osCommand.Printf("CREATE TEMPORARY TABLE %s()", pszTableEscaped);
480
0
        }
481
1.06k
        else
482
1.06k
        {
483
1.06k
            osCommand.Printf("CREATE%s TABLE %s.%s()",
484
1.06k
                             CPLFetchBool(papszOptions, "UNLOGGED", false)
485
1.06k
                                 ? " UNLOGGED"
486
1.06k
                                 : "",
487
1.06k
                             pszSchemaEscaped, pszTableEscaped);
488
1.06k
        }
489
1.06k
        Log(osCommand);
490
1.06k
    }
491
492
    /* -------------------------------------------------------------------- */
493
    /*      Add FID if needed.                                              */
494
    /* -------------------------------------------------------------------- */
495
1.06k
    const char *pszFIDColumnNameIn = CSLFetchNameValue(papszOptions, "FID");
496
1.06k
    CPLString osFIDColumnName;
497
1.06k
    if (pszFIDColumnNameIn == nullptr)
498
1.06k
        osFIDColumnName = "ogc_fid";
499
0
    else
500
0
    {
501
0
        if (bLaunder)
502
0
        {
503
0
            char *pszLaunderedFid = OGRPGCommonLaunderName(
504
0
                pszFIDColumnNameIn, "PGDump", bUTF8ToASCII);
505
0
            osFIDColumnName = pszLaunderedFid;
506
0
            CPLFree(pszLaunderedFid);
507
0
        }
508
0
        else
509
0
        {
510
0
            osFIDColumnName = pszFIDColumnNameIn;
511
0
        }
512
0
    }
513
1.06k
    const CPLString osFIDColumnNameEscaped =
514
1.06k
        OGRPGDumpEscapeColumnName(osFIDColumnName);
515
516
1.06k
    const bool bFID64 = CPLFetchBool(papszOptions, "FID64", false);
517
1.06k
    const char *pszSerialType = bFID64 ? "BIGSERIAL" : "SERIAL";
518
519
1.06k
    if (bCreateTable && !osFIDColumnName.empty())
520
1.06k
    {
521
1.06k
        std::string osConstraintName(osTable);
522
1.06k
        if (CPLStrlenUTF8Ex(osTable.c_str()) + strlen("_pk") >
523
1.06k
            static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
524
23
        {
525
23
            osConstraintName.clear();
526
23
            size_t iUTF8Char = 0;
527
1.41k
            for (size_t i = 0; i < osTable.size(); ++i)
528
1.41k
            {
529
1.41k
                if ((osTable[i] & 0xc0) != 0x80)
530
1.40k
                {
531
1.40k
                    ++iUTF8Char;
532
1.40k
                    if (iUTF8Char == OGR_PG_NAMEDATALEN - strlen("_pk"))
533
23
                        break;
534
1.40k
                }
535
1.39k
                osConstraintName += osTable[i];
536
1.39k
            }
537
23
        }
538
1.06k
        osConstraintName += "_pk";
539
1.06k
        osCommand.Printf(
540
1.06k
            "ALTER TABLE %s.%s ADD COLUMN %s %s "
541
1.06k
            "CONSTRAINT %s PRIMARY KEY",
542
1.06k
            pszSchemaEscaped, pszTableEscaped, osFIDColumnNameEscaped.c_str(),
543
1.06k
            pszSerialType,
544
1.06k
            OGRPGDumpEscapeColumnName(osConstraintName.c_str()).c_str());
545
1.06k
        Log(osCommand);
546
1.06k
    }
547
548
    /* -------------------------------------------------------------------- */
549
    /*      Create geometry/geography column (actual creation possibly      */
550
    /*      deferred).                                                      */
551
    /* -------------------------------------------------------------------- */
552
1.06k
    std::vector<std::string> aosGeomCommands;
553
1.06k
    if (bCreateTable && eType != wkbNone && EQUAL(pszGeomType, "geography"))
554
0
    {
555
0
        if (CSLFetchNameValue(papszOptions, "GEOMETRY_NAME") != nullptr)
556
0
            pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
557
0
        else
558
0
            pszGFldName = "the_geog";
559
560
0
        const char *suffix = "";
561
0
        if ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) &&
562
0
            (nGeometryTypeFlags & OGRGeometry::OGR_G_3D))
563
0
        {
564
0
            suffix = "ZM";
565
0
        }
566
0
        else if ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
567
0
        {
568
0
            suffix = "M";
569
0
        }
570
0
        else if ((nGeometryTypeFlags & OGRGeometry::OGR_G_3D))
571
0
        {
572
0
            suffix = "Z";
573
0
        }
574
575
0
        if (nSRSId)
576
0
            osCommand.Printf("ALTER TABLE %s.%s "
577
0
                             "ADD COLUMN %s geography(%s%s,%d)",
578
0
                             pszSchemaEscaped, pszTableEscaped,
579
0
                             OGRPGDumpEscapeColumnName(pszGFldName).c_str(),
580
0
                             pszGeometryType, suffix, nSRSId);
581
0
        else
582
0
            osCommand.Printf("ALTER TABLE %s.%s "
583
0
                             "ADD COLUMN %s geography(%s%s)",
584
0
                             pszSchemaEscaped, pszTableEscaped,
585
0
                             OGRPGDumpEscapeColumnName(pszGFldName).c_str(),
586
0
                             pszGeometryType, suffix);
587
0
        aosGeomCommands.push_back(osCommand);
588
0
    }
589
1.06k
    else if (bCreateTable && eType != wkbNone)
590
188
    {
591
188
        const char *suffix = "";
592
188
        if (nGeometryTypeFlags ==
593
188
                static_cast<int>(OGRGeometry::OGR_G_MEASURED) &&
594
0
            wkbFlatten(eType) != wkbUnknown)
595
0
        {
596
0
            suffix = "M";
597
0
        }
598
599
188
        osCommand.Printf(
600
188
            "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
601
188
            OGRPGDumpEscapeString(bTemporary ? "" : osSchema.c_str()).c_str(),
602
188
            pszEscapedTableNameSingleQuote,
603
188
            OGRPGDumpEscapeString(pszGFldName).c_str(), nSRSId, pszGeometryType,
604
188
            suffix, nDimension);
605
188
        aosGeomCommands.push_back(osCommand);
606
188
    }
607
608
1.06k
    const char *pszSI =
609
1.06k
        CSLFetchNameValueDef(papszOptions, "SPATIAL_INDEX", "GIST");
610
1.06k
    const bool bCreateSpatialIndex =
611
1.06k
        (EQUAL(pszSI, "GIST") || EQUAL(pszSI, "SPGIST") ||
612
0
         EQUAL(pszSI, "BRIN") || EQUAL(pszSI, "YES") || EQUAL(pszSI, "ON") ||
613
0
         EQUAL(pszSI, "TRUE"));
614
1.06k
    if (!bCreateSpatialIndex && !EQUAL(pszSI, "NO") && !EQUAL(pszSI, "OFF") &&
615
0
        !EQUAL(pszSI, "FALSE") && !EQUAL(pszSI, "NONE"))
616
0
    {
617
0
        CPLError(CE_Warning, CPLE_NotSupported,
618
0
                 "SPATIAL_INDEX=%s not supported", pszSI);
619
0
    }
620
1.06k
    const char *pszSpatialIndexType = EQUAL(pszSI, "SPGIST") ? "SPGIST"
621
1.06k
                                      : EQUAL(pszSI, "BRIN") ? "BRIN"
622
1.06k
                                                             : "GIST";
623
624
1.06k
    std::vector<std::string> aosSpatialIndexCreationCommands;
625
1.06k
    if (bCreateTable && bCreateSpatialIndex && pszGFldName && eType != wkbNone)
626
188
    {
627
188
        const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
628
188
            osTable.c_str(), pszGFldName, 0));
629
630
        /* --------------------------------------------------------------- */
631
        /*      Create the spatial index.                                  */
632
        /* --------------------------------------------------------------- */
633
188
        osCommand.Printf("CREATE INDEX %s "
634
188
                         "ON %s.%s "
635
188
                         "USING %s (%s)",
636
188
                         OGRPGDumpEscapeColumnName(osIndexName.c_str()).c_str(),
637
188
                         pszSchemaEscaped, pszTableEscaped, pszSpatialIndexType,
638
188
                         OGRPGDumpEscapeColumnName(pszGFldName).c_str());
639
188
        aosSpatialIndexCreationCommands.push_back(std::move(osCommand));
640
188
    }
641
642
    /* -------------------------------------------------------------------- */
643
    /*      Create the layer object.                                        */
644
    /* -------------------------------------------------------------------- */
645
1.06k
    const bool bWriteAsHex =
646
1.06k
        !CPLFetchBool(papszOptions, "WRITE_EWKT_GEOM", false);
647
648
1.06k
    auto poLayer = std::make_unique<OGRPGDumpLayer>(
649
1.06k
        this, osSchema.c_str(), osTable.c_str(),
650
1.06k
        !osFIDColumnName.empty() ? osFIDColumnName.c_str() : nullptr,
651
1.06k
        bWriteAsHex, bCreateTable, bSkipConflicts);
652
1.06k
    poLayer->SetLaunderFlag(bLaunder);
653
1.06k
    poLayer->SetUTF8ToASCIIFlag(bUTF8ToASCII);
654
1.06k
    poLayer->SetPrecisionFlag(CPLFetchBool(papszOptions, "PRECISION", true));
655
656
1.06k
    const char *pszOverrideColumnTypes =
657
1.06k
        CSLFetchNameValue(papszOptions, "COLUMN_TYPES");
658
1.06k
    poLayer->SetOverrideColumnTypes(pszOverrideColumnTypes);
659
1.06k
    poLayer->SetUnknownSRSId(nUnknownSRSId);
660
1.06k
    poLayer->SetForcedSRSId(nForcedSRSId);
661
1.06k
    poLayer->SetCreateSpatialIndex(bCreateSpatialIndex, pszSpatialIndexType);
662
1.06k
    poLayer->SetPostGISVersion(nPostGISMajor, nPostGISMinor);
663
1.06k
    poLayer->SetForcedGeometryTypeFlags(nForcedGeometryTypeFlags);
664
665
    // Log geometry field creation immediately or defer it, according to
666
    // GEOM_COLUMN_POSITION
667
1.06k
    const bool bGeomColumnPositionImmediate = EQUAL(
668
1.06k
        CSLFetchNameValueDef(papszOptions, "GEOM_COLUMN_POSITION", "IMMEDIATE"),
669
1.06k
        "IMMEDIATE");
670
1.06k
    poLayer->SetGeomColumnPositionImmediate(bGeomColumnPositionImmediate);
671
1.06k
    if (bGeomColumnPositionImmediate)
672
1.06k
    {
673
1.06k
        for (const auto &osSQL : aosGeomCommands)
674
188
            Log(osSQL.c_str());
675
1.06k
    }
676
0
    else
677
0
    {
678
0
        poLayer->SetDeferredGeomFieldCreationCommands(aosGeomCommands);
679
0
    }
680
1.06k
    poLayer->SetSpatialIndexCreationCommands(aosSpatialIndexCreationCommands);
681
682
1.06k
    const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
683
1.06k
    if (pszDescription != nullptr)
684
0
        poLayer->SetForcedDescription(pszDescription);
685
686
1.06k
    if (eType != wkbNone)
687
188
    {
688
188
        OGRGeomFieldDefn oTmp(pszGFldName, eType);
689
188
        auto poGeomField = std::make_unique<OGRPGDumpGeomFieldDefn>(&oTmp);
690
188
        poGeomField->m_nSRSId = nSRSId;
691
188
        poGeomField->m_nGeometryTypeFlags = nGeometryTypeFlags;
692
188
        poLayer->GetLayerDefn()->AddGeomFieldDefn(std::move(poGeomField));
693
188
    }
694
876
    else if (pszGFldName)
695
144
        poLayer->SetGeometryFieldName(pszGFldName);
696
697
    /* -------------------------------------------------------------------- */
698
    /*      Add layer to data source layer list.                            */
699
    /* -------------------------------------------------------------------- */
700
1.06k
    m_apoLayers.emplace_back(std::move(poLayer));
701
702
1.06k
    return m_apoLayers.back().get();
703
1.06k
}
704
705
/************************************************************************/
706
/*                           TestCapability()                           */
707
/************************************************************************/
708
709
int OGRPGDumpDataSource::TestCapability(const char *pszCap) const
710
711
2.82k
{
712
2.82k
    if (EQUAL(pszCap, ODsCCreateLayer))
713
1.06k
        return TRUE;
714
1.76k
    else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
715
1.47k
        return TRUE;
716
287
    else if (EQUAL(pszCap, ODsCCurveGeometries))
717
9
        return TRUE;
718
278
    else if (EQUAL(pszCap, ODsCMeasuredGeometries))
719
0
        return TRUE;
720
278
    else if (EQUAL(pszCap, ODsCZGeometries))
721
0
        return TRUE;
722
278
    else if (EQUAL(pszCap, ODsCRandomLayerWrite))
723
0
        return TRUE;
724
278
    else
725
278
        return FALSE;
726
2.82k
}
727
728
/************************************************************************/
729
/*                              GetLayer()                              */
730
/************************************************************************/
731
732
const OGRLayer *OGRPGDumpDataSource::GetLayer(int iLayer) const
733
734
15.0k
{
735
15.0k
    if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
736
0
        return nullptr;
737
15.0k
    else
738
15.0k
        return m_apoLayers[iLayer].get();
739
15.0k
}
740
741
/************************************************************************/
742
/*                                Log()                                 */
743
/************************************************************************/
744
745
bool OGRPGDumpDataSource::Log(const char *pszStr, bool bAddSemiColumn)
746
205k
{
747
205k
    if (m_fp == nullptr)
748
0
    {
749
0
        return false;
750
0
    }
751
752
205k
    VSIFWriteL(pszStr, strlen(pszStr), 1, m_fp);
753
205k
    if (bAddSemiColumn)
754
205k
    {
755
205k
        const char chSemiColumn = ';';
756
205k
        VSIFWriteL(&chSemiColumn, 1, 1, m_fp);
757
205k
    }
758
205k
    VSIFWriteL(m_pszEOL, strlen(m_pszEOL), 1, m_fp);
759
205k
    return true;
760
205k
}
761
762
/************************************************************************/
763
/*                             StartCopy()                              */
764
/************************************************************************/
765
void OGRPGDumpDataSource::StartCopy(OGRPGDumpLayer *poPGLayer)
766
0
{
767
0
    EndCopy();
768
0
    m_poLayerInCopyMode = poPGLayer;
769
0
}
770
771
/************************************************************************/
772
/*                              EndCopy()                               */
773
/************************************************************************/
774
OGRErr OGRPGDumpDataSource::EndCopy()
775
1.62k
{
776
1.62k
    if (m_poLayerInCopyMode != nullptr)
777
0
    {
778
0
        OGRErr result = m_poLayerInCopyMode->EndCopy();
779
0
        m_poLayerInCopyMode = nullptr;
780
781
0
        return result;
782
0
    }
783
784
1.62k
    return OGRERR_NONE;
785
1.62k
}