Coverage Report

Created: 2026-11-12 08:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumplayer.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implements OGRPGDumpLayer 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 "ogr_pgdump.h"
14
#include "cpl_conv.h"
15
#include "cpl_md5.h"
16
#include "cpl_string.h"
17
#include "ogr_p.h"
18
19
#include <cmath>
20
#include <limits>
21
22
//
23
static CPLString
24
OGRPGDumpEscapeStringList(char **papszItems, bool bForInsertOrUpdate,
25
                          OGRPGCommonEscapeStringCbk pfnEscapeString,
26
                          void *userdata);
27
28
static CPLString OGRPGDumpEscapeStringWithUserData(
29
    CPL_UNUSED void *user_data, const char *pszStrValue, int nMaxLength,
30
    CPL_UNUSED const char *pszLayerName, const char *pszFieldName)
31
229k
{
32
229k
    return OGRPGDumpEscapeString(pszStrValue, nMaxLength, pszFieldName);
33
229k
}
34
35
2.77k
OGRPGDumpGeomFieldDefn::~OGRPGDumpGeomFieldDefn() = default;
36
37
/************************************************************************/
38
/*                           OGRPGDumpLayer()                           */
39
/************************************************************************/
40
41
OGRPGDumpLayer::OGRPGDumpLayer(OGRPGDumpDataSource *poDSIn,
42
                               const char *pszSchemaNameIn,
43
                               const char *pszTableName,
44
                               const char *pszFIDColumnIn, int bWriteAsHexIn,
45
                               int bCreateTableIn, bool bSkipConflictsIn)
46
1.09k
    : m_pszSchemaName(CPLStrdup(pszSchemaNameIn)),
47
1.09k
      m_pszSqlTableName(CPLStrdup(CPLString().Printf(
48
1.09k
          "%s.%s", OGRPGDumpEscapeColumnName(m_pszSchemaName).c_str(),
49
1.09k
          OGRPGDumpEscapeColumnName(pszTableName).c_str()))),
50
1.09k
      m_pszFIDColumn(pszFIDColumnIn ? CPLStrdup(pszFIDColumnIn) : nullptr),
51
1.09k
      m_poFeatureDefn(new OGRFeatureDefn(pszTableName)), m_poDS(poDSIn),
52
1.09k
      m_bWriteAsHex(CPL_TO_BOOL(bWriteAsHexIn)), m_bCreateTable(bCreateTableIn),
53
1.09k
      m_bSkipConflicts(bSkipConflictsIn)
54
1.09k
{
55
1.09k
    SetDescription(m_poFeatureDefn->GetName());
56
1.09k
    m_poFeatureDefn->SetGeomType(wkbNone);
57
1.09k
    m_poFeatureDefn->Reference();
58
1.09k
}
59
60
/************************************************************************/
61
/*                          ~OGRPGDumpLayer()                           */
62
/************************************************************************/
63
64
OGRPGDumpLayer::~OGRPGDumpLayer()
65
1.09k
{
66
1.09k
    EndCopy();
67
1.09k
    LogDeferredFieldCreationIfNeeded();
68
1.09k
    UpdateSequenceIfNeeded();
69
1.09k
    for (const auto &osSQL : m_aosSpatialIndexCreationCommands)
70
2.77k
    {
71
2.77k
        m_poDS->Log(osSQL.c_str());
72
2.77k
    }
73
74
1.09k
    m_poFeatureDefn->Release();
75
1.09k
    CPLFree(m_pszSchemaName);
76
1.09k
    CPLFree(m_pszSqlTableName);
77
1.09k
    CPLFree(m_pszFIDColumn);
78
1.09k
}
79
80
/************************************************************************/
81
/*                           GetNextFeature()                           */
82
/************************************************************************/
83
84
OGRFeature *OGRPGDumpLayer::GetNextFeature()
85
0
{
86
0
    CPLError(CE_Failure, CPLE_NotSupported, "PGDump driver is write only");
87
0
    return nullptr;
88
0
}
89
90
/************************************************************************/
91
/*                           GetNextFeature()                           */
92
/************************************************************************/
93
94
bool OGRPGDumpLayer::TestCapability(const char *pszCap) const
95
5.30k
{
96
5.30k
    if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCCreateField) ||
97
5.30k
        EQUAL(pszCap, OLCCreateGeomField) ||
98
5.30k
        EQUAL(pszCap, OLCCurveGeometries) || EQUAL(pszCap, OLCZGeometries) ||
99
2.04k
        EQUAL(pszCap, OLCMeasuredGeometries))
100
5.30k
        return TRUE;
101
0
    else
102
0
        return FALSE;
103
5.30k
}
104
105
/************************************************************************/
106
/*                  LogDeferredFieldCreationIfNeeded()                  */
107
/************************************************************************/
108
109
void OGRPGDumpLayer::LogDeferredFieldCreationIfNeeded()
110
196k
{
111
    // Emit column creation
112
196k
    if (!m_aosDeferrentNonGeomFieldCreationCommands.empty() ||
113
196k
        !m_aosDeferredGeomFieldCreationCommands.empty())
114
0
    {
115
0
        CPLAssert(m_bCreateTable);
116
0
        CPLAssert(!m_bGeomColumnPositionImmediate);
117
        // In non-immediate mode, we put geometry fields after non-geometry
118
        // ones
119
0
        for (const auto &osSQL : m_aosDeferrentNonGeomFieldCreationCommands)
120
0
            m_poDS->Log(osSQL.c_str());
121
0
        for (const auto &osSQL : m_aosDeferredGeomFieldCreationCommands)
122
0
            m_poDS->Log(osSQL.c_str());
123
0
        m_aosDeferrentNonGeomFieldCreationCommands.clear();
124
0
        m_aosDeferredGeomFieldCreationCommands.clear();
125
0
    }
126
196k
}
127
128
/************************************************************************/
129
/*                           GetNextFeature()                           */
130
/************************************************************************/
131
132
OGRErr OGRPGDumpLayer::ICreateFeature(OGRFeature *poFeature)
133
194k
{
134
194k
    if (nullptr == poFeature)
135
0
    {
136
0
        CPLError(CE_Failure, CPLE_AppDefined,
137
0
                 "NULL pointer to OGRFeature passed to CreateFeature().");
138
0
        return OGRERR_FAILURE;
139
0
    }
140
141
194k
    LogDeferredFieldCreationIfNeeded();
142
143
    /* In case the FID column has also been created as a regular field */
144
194k
    if (m_iFIDAsRegularColumnIndex >= 0)
145
0
    {
146
0
        if (poFeature->GetFID() == OGRNullFID)
147
0
        {
148
0
            if (poFeature->IsFieldSetAndNotNull(m_iFIDAsRegularColumnIndex))
149
0
            {
150
0
                poFeature->SetFID(
151
0
                    poFeature->GetFieldAsInteger64(m_iFIDAsRegularColumnIndex));
152
0
            }
153
0
        }
154
0
        else
155
0
        {
156
0
            if (!poFeature->IsFieldSetAndNotNull(m_iFIDAsRegularColumnIndex) ||
157
0
                poFeature->GetFieldAsInteger64(m_iFIDAsRegularColumnIndex) !=
158
0
                    poFeature->GetFID())
159
0
            {
160
0
                CPLError(CE_Failure, CPLE_AppDefined,
161
0
                         "Inconsistent values of FID and field of same name");
162
0
                return OGRERR_FAILURE;
163
0
            }
164
0
        }
165
0
    }
166
167
194k
    if (!poFeature->Validate((OGR_F_VAL_ALL & ~OGR_F_VAL_WIDTH) |
168
194k
                                 OGR_F_VAL_ALLOW_DIFFERENT_GEOM_DIM,
169
194k
                             TRUE))
170
2
        return OGRERR_FAILURE;
171
172
    // We avoid testing the config option too often.
173
194k
    if (m_bUseCopy == USE_COPY_UNSET)
174
811
        m_bUseCopy = CPLTestBool(CPLGetConfigOption("PG_USE_COPY", "NO"));
175
176
194k
    OGRErr eErr;
177
194k
    if (!m_bUseCopy)
178
194k
    {
179
194k
        eErr = CreateFeatureViaInsert(poFeature);
180
194k
    }
181
0
    else
182
0
    {
183
        // If there's a unset field with a default value, then we must use a
184
        // specific INSERT statement to avoid unset fields to be bound to NULL.
185
0
        bool bHasDefaultValue = false;
186
0
        const int nFieldCount = m_poFeatureDefn->GetFieldCount();
187
0
        for (int iField = 0; iField < nFieldCount; iField++)
188
0
        {
189
0
            if (!poFeature->IsFieldSetAndNotNull(iField) &&
190
0
                poFeature->GetFieldDefnRef(iField)->GetDefault() != nullptr)
191
0
            {
192
0
                bHasDefaultValue = true;
193
0
                break;
194
0
            }
195
0
        }
196
0
        if (bHasDefaultValue)
197
0
        {
198
0
            EndCopy();
199
0
            eErr = CreateFeatureViaInsert(poFeature);
200
0
        }
201
0
        else
202
0
        {
203
0
            const bool bFIDSet = poFeature->GetFID() != OGRNullFID;
204
0
            if (m_bCopyActive && bFIDSet != m_bCopyStatementWithFID)
205
0
            {
206
0
                EndCopy();
207
0
                eErr = CreateFeatureViaInsert(poFeature);
208
0
            }
209
0
            else
210
0
            {
211
0
                if (!m_bCopyActive)
212
0
                {
213
                    // This is a heuristics. If the first feature to be copied
214
                    // has a FID set (and that a FID column has been
215
                    // identified), then we will try to copy FID values from
216
                    // features. Otherwise, we will not do and assume that the
217
                    // FID column is an autoincremented column.
218
0
                    StartCopy(bFIDSet);
219
0
                    m_bCopyStatementWithFID = bFIDSet;
220
0
                    m_bNeedToUpdateSequence = bFIDSet;
221
0
                }
222
223
0
                eErr = CreateFeatureViaCopy(poFeature);
224
0
                if (bFIDSet)
225
0
                    m_bAutoFIDOnCreateViaCopy = false;
226
0
                if (eErr == OGRERR_NONE && m_bAutoFIDOnCreateViaCopy)
227
0
                {
228
0
                    poFeature->SetFID(++m_iNextShapeId);
229
0
                }
230
0
            }
231
0
        }
232
0
    }
233
234
194k
    if (eErr == OGRERR_NONE && m_iFIDAsRegularColumnIndex >= 0)
235
0
    {
236
0
        poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID());
237
0
    }
238
194k
    return eErr;
239
194k
}
240
241
/************************************************************************/
242
/*                       CreateFeatureViaInsert()                       */
243
/************************************************************************/
244
245
OGRErr OGRPGDumpLayer::CreateFeatureViaInsert(OGRFeature *poFeature)
246
247
194k
{
248
194k
    OGRErr eErr = OGRERR_FAILURE;
249
250
194k
    if (nullptr == poFeature)
251
0
    {
252
0
        CPLError(
253
0
            CE_Failure, CPLE_AppDefined,
254
0
            "NULL pointer to OGRFeature passed to CreateFeatureViaInsert().");
255
0
        return eErr;
256
0
    }
257
258
    /* -------------------------------------------------------------------- */
259
    /*      Form the INSERT command.                                        */
260
    /* -------------------------------------------------------------------- */
261
194k
    CPLString osCommand;
262
194k
    osCommand.Printf("INSERT INTO %s (", m_pszSqlTableName);
263
264
194k
    bool bNeedComma = false;
265
266
194k
    if (poFeature->GetFID() != OGRNullFID && m_pszFIDColumn != nullptr)
267
66.9k
    {
268
66.9k
        m_bNeedToUpdateSequence = true;
269
270
66.9k
        osCommand += OGRPGDumpEscapeColumnName(m_pszFIDColumn);
271
66.9k
        bNeedComma = true;
272
66.9k
    }
273
128k
    else
274
128k
    {
275
128k
        UpdateSequenceIfNeeded();
276
128k
    }
277
278
194k
    const auto AddGeomFieldsName = [this, poFeature, &bNeedComma, &osCommand]()
279
194k
    {
280
382k
        for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
281
187k
        {
282
187k
            OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
283
187k
            if (poGeom != nullptr)
284
9.45k
            {
285
9.45k
                if (bNeedComma)
286
7.31k
                    osCommand += ", ";
287
288
9.45k
                const OGRGeomFieldDefn *poGFldDefn =
289
9.45k
                    poFeature->GetGeomFieldDefnRef(i);
290
9.45k
                osCommand +=
291
9.45k
                    OGRPGDumpEscapeColumnName(poGFldDefn->GetNameRef());
292
9.45k
                bNeedComma = true;
293
9.45k
            }
294
187k
        }
295
194k
    };
296
297
194k
    if (m_bGeomColumnPositionImmediate)
298
194k
        AddGeomFieldsName();
299
300
1.49M
    for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
301
1.30M
    {
302
1.30M
        if (i == m_iFIDAsRegularColumnIndex)
303
0
            continue;
304
1.30M
        if (!poFeature->IsFieldSet(i))
305
1.07M
            continue;
306
307
229k
        if (!bNeedComma)
308
117k
            bNeedComma = true;
309
111k
        else
310
111k
            osCommand += ", ";
311
312
229k
        osCommand += OGRPGDumpEscapeColumnName(
313
229k
            m_poFeatureDefn->GetFieldDefn(i)->GetNameRef());
314
229k
    }
315
316
194k
    if (!m_bGeomColumnPositionImmediate)
317
0
        AddGeomFieldsName();
318
319
194k
    const bool bEmptyInsert = !bNeedComma;
320
321
194k
    osCommand += ") VALUES (";
322
323
194k
    bNeedComma = false;
324
325
    /* Set the geometry */
326
194k
    const auto AddGeomFieldsValue = [this, poFeature, &bNeedComma, &osCommand]()
327
194k
    {
328
382k
        for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
329
187k
        {
330
187k
            OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
331
187k
            if (poGeom != nullptr)
332
9.45k
            {
333
9.45k
                char *pszWKT = nullptr;
334
335
9.45k
                const OGRPGDumpGeomFieldDefn *poGFldDefn =
336
9.45k
                    cpl::down_cast<const OGRPGDumpGeomFieldDefn *>(
337
9.45k
                        poFeature->GetGeomFieldDefnRef(i));
338
339
9.45k
                poGeom->closeRings();
340
9.45k
                poGeom->set3D(poGFldDefn->m_nGeometryTypeFlags &
341
9.45k
                              OGRGeometry::OGR_G_3D);
342
9.45k
                poGeom->setMeasured(poGFldDefn->m_nGeometryTypeFlags &
343
9.45k
                                    OGRGeometry::OGR_G_MEASURED);
344
345
9.45k
                if (bNeedComma)
346
7.31k
                    osCommand += ", ";
347
348
9.45k
                if (m_bWriteAsHex)
349
9.45k
                {
350
9.45k
                    char *pszHex =
351
9.45k
                        OGRGeometryToHexEWKB(poGeom, poGFldDefn->m_nSRSId,
352
9.45k
                                             m_nPostGISMajor, m_nPostGISMinor);
353
9.45k
                    osCommand += "'";
354
9.45k
                    if (!pszHex || pszHex[0] == 0)
355
0
                    {
356
0
                        CPLFree(pszHex);
357
0
                        return false;
358
0
                    }
359
9.45k
                    try
360
9.45k
                    {
361
9.45k
                        osCommand += pszHex;
362
9.45k
                    }
363
9.45k
                    catch (const std::bad_alloc &)
364
9.45k
                    {
365
0
                        CPLError(CE_Failure, CPLE_OutOfMemory,
366
0
                                 "Out of memory: too large geometry");
367
0
                        CPLFree(pszHex);
368
0
                        return false;
369
0
                    }
370
9.45k
                    osCommand += "'";
371
9.45k
                    CPLFree(pszHex);
372
9.45k
                }
373
0
                else
374
0
                {
375
0
                    poGeom->exportToWkt(&pszWKT, wkbVariantIso);
376
377
0
                    if (!pszWKT)
378
0
                    {
379
0
                        return false;
380
0
                    }
381
0
                    try
382
0
                    {
383
0
                        osCommand += CPLSPrintf("GeomFromEWKT('SRID=%d;",
384
0
                                                poGFldDefn->m_nSRSId);
385
0
                        osCommand += pszWKT;
386
0
                        osCommand += "'::TEXT) ";
387
0
                    }
388
0
                    catch (const std::bad_alloc &)
389
0
                    {
390
0
                        CPLError(CE_Failure, CPLE_OutOfMemory,
391
0
                                 "Out of memory: too large geometry");
392
0
                        CPLFree(pszWKT);
393
0
                        return false;
394
0
                    }
395
0
                    CPLFree(pszWKT);
396
0
                }
397
398
9.45k
                bNeedComma = true;
399
9.45k
            }
400
187k
        }
401
194k
        return true;
402
194k
    };
403
404
    /* Set the FID */
405
194k
    if (poFeature->GetFID() != OGRNullFID && m_pszFIDColumn != nullptr)
406
66.9k
    {
407
66.9k
        if (bNeedComma)
408
0
            osCommand += ", ";
409
66.9k
        osCommand += CPLString().Printf(CPL_FRMT_GIB, poFeature->GetFID());
410
66.9k
        bNeedComma = true;
411
66.9k
    }
412
413
194k
    if (m_bGeomColumnPositionImmediate)
414
194k
    {
415
194k
        if (!AddGeomFieldsValue())
416
0
            return OGRERR_FAILURE;
417
194k
    }
418
419
1.49M
    for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
420
1.30M
    {
421
1.30M
        if (i == m_iFIDAsRegularColumnIndex)
422
0
            continue;
423
1.30M
        if (!poFeature->IsFieldSet(i))
424
1.07M
            continue;
425
426
229k
        if (bNeedComma)
427
111k
            osCommand += ", ";
428
117k
        else
429
117k
            bNeedComma = true;
430
431
229k
        OGRPGCommonAppendFieldValue(osCommand, poFeature, i,
432
229k
                                    OGRPGDumpEscapeStringWithUserData, nullptr);
433
229k
    }
434
435
194k
    if (!m_bGeomColumnPositionImmediate)
436
0
    {
437
0
        if (!AddGeomFieldsValue())
438
0
            return OGRERR_FAILURE;
439
0
    }
440
441
194k
    osCommand += ")";
442
443
194k
    if (m_bSkipConflicts)
444
0
        osCommand += " ON CONFLICT DO NOTHING";
445
446
194k
    if (bEmptyInsert)
447
7.94k
        osCommand.Printf("INSERT INTO %s DEFAULT VALUES", m_pszSqlTableName);
448
449
    /* -------------------------------------------------------------------- */
450
    /*      Execute the insert.                                             */
451
    /* -------------------------------------------------------------------- */
452
194k
    m_poDS->Log(osCommand);
453
454
194k
    if (poFeature->GetFID() == OGRNullFID)
455
128k
        poFeature->SetFID(++m_iNextShapeId);
456
457
194k
    return OGRERR_NONE;
458
194k
}
459
460
/************************************************************************/
461
/*                        CreateFeatureViaCopy()                        */
462
/************************************************************************/
463
464
OGRErr OGRPGDumpLayer::CreateFeatureViaCopy(OGRFeature *poFeature)
465
0
{
466
0
    CPLString osCommand;
467
468
0
    if (m_bFIDColumnInCopyFields)
469
0
        OGRPGCommonAppendCopyFID(osCommand, poFeature);
470
471
0
    const auto AddGeomFieldsValue = [this, poFeature, &osCommand]()
472
0
    {
473
0
        for (int i = 0; i < poFeature->GetGeomFieldCount(); i++)
474
0
        {
475
0
            OGRGeometry *poGeometry = poFeature->GetGeomFieldRef(i);
476
0
            char *pszGeom = nullptr;
477
0
            if (nullptr !=
478
0
                poGeometry /* && (bHasWkb || bHasPostGISGeometry || bHasPostGISGeography) */)
479
0
            {
480
0
                const OGRPGDumpGeomFieldDefn *poGFldDefn =
481
0
                    cpl::down_cast<const OGRPGDumpGeomFieldDefn *>(
482
0
                        poFeature->GetGeomFieldDefnRef(i));
483
484
0
                poGeometry->closeRings();
485
0
                poGeometry->set3D(poGFldDefn->m_nGeometryTypeFlags &
486
0
                                  OGRGeometry::OGR_G_3D);
487
0
                poGeometry->setMeasured(poGFldDefn->m_nGeometryTypeFlags &
488
0
                                        OGRGeometry::OGR_G_MEASURED);
489
490
0
                pszGeom =
491
0
                    OGRGeometryToHexEWKB(poGeometry, poGFldDefn->m_nSRSId,
492
0
                                         m_nPostGISMajor, m_nPostGISMinor);
493
0
            }
494
495
0
            if (!osCommand.empty())
496
0
                osCommand += "\t";
497
0
            if (!pszGeom || pszGeom[0] == 0)
498
0
            {
499
0
                CPLFree(pszGeom);
500
0
                return false;
501
0
            }
502
0
            try
503
0
            {
504
0
                osCommand += pszGeom;
505
0
            }
506
0
            catch (const std::bad_alloc &)
507
0
            {
508
0
                CPLError(CE_Failure, CPLE_OutOfMemory,
509
0
                         "Out of memory: too large geometry");
510
0
                CPLFree(pszGeom);
511
0
                return false;
512
0
            }
513
0
            CPLFree(pszGeom);
514
0
        }
515
0
        return true;
516
0
    };
517
518
0
    if (m_bGeomColumnPositionImmediate)
519
0
    {
520
0
        if (!AddGeomFieldsValue())
521
0
            return OGRERR_FAILURE;
522
0
    }
523
524
0
    OGRPGCommonAppendCopyRegularFields(
525
0
        osCommand, poFeature, m_pszFIDColumn,
526
0
        std::vector<bool>(m_poFeatureDefn->GetFieldCount(), true),
527
0
        OGRPGDumpEscapeStringWithUserData, nullptr);
528
529
0
    if (!m_bGeomColumnPositionImmediate)
530
0
    {
531
0
        if (!AddGeomFieldsValue())
532
0
            return OGRERR_FAILURE;
533
0
    }
534
535
    /* ------------------------------------------------------------ */
536
    /*      Execute the copy.                                       */
537
    /* ------------------------------------------------------------ */
538
539
0
    OGRErr result = OGRERR_NONE;
540
541
0
    m_poDS->Log(osCommand, false);
542
543
0
    return result;
544
0
}
545
546
/************************************************************************/
547
/*                      OGRPGCommonAppendCopyFID()                      */
548
/************************************************************************/
549
550
void OGRPGCommonAppendCopyFID(CPLString &osCommand, OGRFeature *poFeature)
551
0
{
552
0
    if (!osCommand.empty())
553
0
        osCommand += "\t";
554
555
    /* Set the FID */
556
0
    if (poFeature->GetFID() != OGRNullFID)
557
0
    {
558
0
        osCommand += CPLString().Printf(CPL_FRMT_GIB, poFeature->GetFID());
559
0
    }
560
0
    else
561
0
    {
562
0
        osCommand += "\\N";
563
0
    }
564
0
}
565
566
/************************************************************************/
567
/*                 OGRPGCommonAppendCopyRegularFields()                 */
568
/************************************************************************/
569
570
void OGRPGCommonAppendCopyRegularFields(
571
    CPLString &osCommand, OGRFeature *poFeature, const char *pszFIDColumn,
572
    const std::vector<bool> &abFieldsToInclude,
573
    OGRPGCommonEscapeStringCbk pfnEscapeString, void *userdata)
574
0
{
575
0
    const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
576
0
    const int nFIDIndex =
577
0
        pszFIDColumn ? poFeatureDefn->GetFieldIndex(pszFIDColumn) : -1;
578
579
0
    const int nFieldCount = poFeatureDefn->GetFieldCount();
580
0
    bool bAddTab = !osCommand.empty();
581
582
0
    CPLAssert(nFieldCount == static_cast<int>(abFieldsToInclude.size()));
583
584
0
    for (int i = 0; i < nFieldCount; i++)
585
0
    {
586
0
        if (i == nFIDIndex)
587
0
            continue;
588
0
        if (!abFieldsToInclude[i])
589
0
            continue;
590
591
0
        const char *pszStrValue = poFeature->GetFieldAsString(i);
592
0
        char *pszNeedToFree = nullptr;
593
594
0
        if (bAddTab)
595
0
            osCommand += "\t";
596
0
        bAddTab = true;
597
598
0
        if (!poFeature->IsFieldSetAndNotNull(i))
599
0
        {
600
0
            osCommand += "\\N";
601
602
0
            continue;
603
0
        }
604
605
0
        const int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
606
607
        // We need special formatting for integer list values.
608
0
        if (nOGRFieldType == OFTIntegerList)
609
0
        {
610
0
            int nCount, nOff = 0;
611
0
            const int *panItems = poFeature->GetFieldAsIntegerList(i, &nCount);
612
613
0
            const size_t nLen = nCount * 13 + 10;
614
0
            pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
615
0
            strcpy(pszNeedToFree, "{");
616
0
            for (int j = 0; j < nCount; j++)
617
0
            {
618
0
                if (j != 0)
619
0
                    strcat(pszNeedToFree + nOff, ",");
620
621
0
                nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
622
0
                snprintf(pszNeedToFree + nOff, nLen - nOff, "%d", panItems[j]);
623
0
            }
624
0
            strcat(pszNeedToFree + nOff, "}");
625
0
            pszStrValue = pszNeedToFree;
626
0
        }
627
628
0
        else if (nOGRFieldType == OFTInteger64List)
629
0
        {
630
0
            int nCount, nOff = 0;
631
0
            const GIntBig *panItems =
632
0
                poFeature->GetFieldAsInteger64List(i, &nCount);
633
634
0
            const size_t nLen = nCount * 26 + 10;
635
0
            pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
636
0
            strcpy(pszNeedToFree, "{");
637
0
            for (int j = 0; j < nCount; j++)
638
0
            {
639
0
                if (j != 0)
640
0
                    strcat(pszNeedToFree + nOff, ",");
641
642
0
                nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
643
0
                snprintf(pszNeedToFree + nOff, nLen - nOff, CPL_FRMT_GIB,
644
0
                         panItems[j]);
645
0
            }
646
0
            strcat(pszNeedToFree + nOff, "}");
647
0
            pszStrValue = pszNeedToFree;
648
0
        }
649
650
        // We need special formatting for real list values.
651
0
        else if (nOGRFieldType == OFTRealList)
652
0
        {
653
0
            int nOff = 0;
654
0
            int nCount = 0;
655
0
            const double *padfItems =
656
0
                poFeature->GetFieldAsDoubleList(i, &nCount);
657
658
0
            const size_t nLen = nCount * 40 + 10;
659
0
            pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
660
0
            strcpy(pszNeedToFree, "{");
661
0
            for (int j = 0; j < nCount; j++)
662
0
            {
663
0
                if (j != 0)
664
0
                    strcat(pszNeedToFree + nOff, ",");
665
666
0
                nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
667
                // Check for special values. They need to be quoted.
668
0
                if (std::isnan(padfItems[j]))
669
0
                    snprintf(pszNeedToFree + nOff, nLen - nOff, "NaN");
670
0
                else if (std::isinf(padfItems[j]))
671
0
                    snprintf(pszNeedToFree + nOff, nLen - nOff,
672
0
                             (padfItems[j] > 0) ? "Infinity" : "-Infinity");
673
0
                else
674
0
                    CPLsnprintf(pszNeedToFree + nOff, nLen - nOff, "%.16g",
675
0
                                padfItems[j]);
676
0
            }
677
0
            strcat(pszNeedToFree + nOff, "}");
678
0
            pszStrValue = pszNeedToFree;
679
0
        }
680
681
        // We need special formatting for string list values.
682
0
        else if (nOGRFieldType == OFTStringList)
683
0
        {
684
0
            CPLString osStr;
685
0
            char **papszItems = poFeature->GetFieldAsStringList(i);
686
687
0
            pszStrValue = pszNeedToFree = CPLStrdup(OGRPGDumpEscapeStringList(
688
0
                papszItems, false, pfnEscapeString, userdata));
689
0
        }
690
691
        // Binary formatting
692
0
        else if (nOGRFieldType == OFTBinary)
693
0
        {
694
0
            int nLen = 0;
695
0
            GByte *pabyData = poFeature->GetFieldAsBinary(i, &nLen);
696
0
            char *pszBytea = OGRPGCommonGByteArrayToBYTEA(pabyData, nLen);
697
698
0
            pszStrValue = pszNeedToFree = pszBytea;
699
0
        }
700
701
0
        else if (nOGRFieldType == OFTReal)
702
0
        {
703
            // Check for special values. They need to be quoted.
704
0
            double dfVal = poFeature->GetFieldAsDouble(i);
705
0
            if (std::isnan(dfVal))
706
0
                pszStrValue = "NaN";
707
0
            else if (std::isinf(dfVal))
708
0
                pszStrValue = (dfVal > 0) ? "Infinity" : "-Infinity";
709
0
        }
710
711
0
        if (nOGRFieldType != OFTIntegerList &&
712
0
            nOGRFieldType != OFTInteger64List && nOGRFieldType != OFTRealList &&
713
0
            nOGRFieldType != OFTInteger && nOGRFieldType != OFTInteger64 &&
714
0
            nOGRFieldType != OFTReal && nOGRFieldType != OFTBinary)
715
0
        {
716
0
            int iUTFChar = 0;
717
0
            const int nMaxWidth = poFeatureDefn->GetFieldDefn(i)->GetWidth();
718
719
0
            for (int iChar = 0; pszStrValue[iChar] != '\0'; iChar++)
720
0
            {
721
                // count of utf chars
722
0
                if (nOGRFieldType != OFTStringList &&
723
0
                    (pszStrValue[iChar] & 0xc0) != 0x80)
724
0
                {
725
0
                    if (nMaxWidth > 0 && iUTFChar == nMaxWidth)
726
0
                    {
727
0
                        CPLDebug("PG",
728
0
                                 "Truncated %s field value, it was too long.",
729
0
                                 poFeatureDefn->GetFieldDefn(i)->GetNameRef());
730
0
                        break;
731
0
                    }
732
0
                    iUTFChar++;
733
0
                }
734
735
                /* Escape embedded \, \t, \n, \r since they will cause COPY
736
                   to misinterpret a line of text and thus abort */
737
0
                if (pszStrValue[iChar] == '\\' || pszStrValue[iChar] == '\t' ||
738
0
                    pszStrValue[iChar] == '\r' || pszStrValue[iChar] == '\n')
739
0
                {
740
0
                    osCommand += '\\';
741
0
                }
742
743
0
                osCommand += pszStrValue[iChar];
744
0
            }
745
0
        }
746
0
        else
747
0
        {
748
0
            osCommand += pszStrValue;
749
0
        }
750
751
0
        if (pszNeedToFree)
752
0
            CPLFree(pszNeedToFree);
753
0
    }
754
0
}
755
756
/************************************************************************/
757
/*                             StartCopy()                              */
758
/************************************************************************/
759
760
OGRErr OGRPGDumpLayer::StartCopy(int bSetFID)
761
762
0
{
763
    /* Tell the datasource we are now planning to copy data */
764
0
    m_poDS->StartCopy(this);
765
766
0
    CPLString osFields = BuildCopyFields(bSetFID);
767
768
0
    size_t size = osFields.size() + strlen(m_pszSqlTableName) + 100;
769
0
    char *pszCommand = static_cast<char *>(CPLMalloc(size));
770
771
0
    snprintf(pszCommand, size, "COPY %s (%s) FROM STDIN", m_pszSqlTableName,
772
0
             osFields.c_str());
773
774
0
    m_poDS->Log(pszCommand);
775
0
    m_bCopyActive = true;
776
777
0
    CPLFree(pszCommand);
778
779
0
    return OGRERR_NONE;
780
0
}
781
782
/************************************************************************/
783
/*                              EndCopy()                               */
784
/************************************************************************/
785
786
OGRErr OGRPGDumpLayer::EndCopy()
787
788
1.09k
{
789
1.09k
    if (!m_bCopyActive)
790
1.09k
        return OGRERR_NONE;
791
792
0
    m_bCopyActive = false;
793
794
0
    m_poDS->Log("\\.", false);
795
796
0
    m_bUseCopy = USE_COPY_UNSET;
797
798
0
    UpdateSequenceIfNeeded();
799
800
0
    return OGRERR_NONE;
801
1.09k
}
802
803
/************************************************************************/
804
/*                       UpdateSequenceIfNeeded()                       */
805
/************************************************************************/
806
807
void OGRPGDumpLayer::UpdateSequenceIfNeeded()
808
129k
{
809
129k
    if (m_bNeedToUpdateSequence && m_pszFIDColumn != nullptr)
810
72
    {
811
72
        CPLString osCommand;
812
72
        osCommand.Printf(
813
72
            "SELECT setval(pg_get_serial_sequence(%s, %s), MAX(%s)) FROM %s",
814
72
            OGRPGDumpEscapeString(m_pszSqlTableName).c_str(),
815
72
            OGRPGDumpEscapeString(m_pszFIDColumn).c_str(),
816
72
            OGRPGDumpEscapeColumnName(m_pszFIDColumn).c_str(),
817
72
            m_pszSqlTableName);
818
72
        m_poDS->Log(osCommand);
819
72
        m_bNeedToUpdateSequence = false;
820
72
    }
821
129k
}
822
823
/************************************************************************/
824
/*                          BuildCopyFields()                           */
825
/************************************************************************/
826
827
CPLString OGRPGDumpLayer::BuildCopyFields(int bSetFID)
828
0
{
829
0
    CPLString osFieldList;
830
831
0
    int nFIDIndex = -1;
832
0
    m_bFIDColumnInCopyFields = m_pszFIDColumn != nullptr && bSetFID;
833
0
    if (m_bFIDColumnInCopyFields)
834
0
    {
835
0
        nFIDIndex = m_poFeatureDefn->GetFieldIndex(m_pszFIDColumn);
836
837
0
        osFieldList += OGRPGDumpEscapeColumnName(m_pszFIDColumn);
838
0
    }
839
840
0
    const auto AddGeomFields = [this, &osFieldList]()
841
0
    {
842
0
        for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
843
0
        {
844
0
            if (!osFieldList.empty())
845
0
                osFieldList += ", ";
846
847
0
            OGRGeomFieldDefn *poGFldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
848
849
0
            osFieldList += OGRPGDumpEscapeColumnName(poGFldDefn->GetNameRef());
850
0
        }
851
0
    };
852
853
0
    if (m_bGeomColumnPositionImmediate)
854
0
        AddGeomFields();
855
856
0
    for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
857
0
    {
858
0
        if (i == nFIDIndex)
859
0
            continue;
860
861
0
        const char *pszName = m_poFeatureDefn->GetFieldDefn(i)->GetNameRef();
862
863
0
        if (!osFieldList.empty())
864
0
            osFieldList += ", ";
865
866
0
        osFieldList += OGRPGDumpEscapeColumnName(pszName);
867
0
    }
868
869
0
    if (!m_bGeomColumnPositionImmediate)
870
0
        AddGeomFields();
871
872
0
    return osFieldList;
873
0
}
874
875
/************************************************************************/
876
/*                     OGRPGDumpEscapeColumnName( )                     */
877
/************************************************************************/
878
879
CPLString OGRPGDumpEscapeColumnName(const char *pszColumnName)
880
352k
{
881
352k
    CPLString osStr = "\"";
882
883
352k
    char ch = '\0';
884
10.4M
    for (int i = 0; (ch = pszColumnName[i]) != '\0'; i++)
885
10.0M
    {
886
10.0M
        if (ch == '"')
887
1.17k
            osStr.append(1, ch);
888
10.0M
        osStr.append(1, ch);
889
10.0M
    }
890
891
352k
    osStr += "\"";
892
893
352k
    return osStr;
894
352k
}
895
896
/************************************************************************/
897
/*                           EscapeString( )                            */
898
/************************************************************************/
899
900
CPLString OGRPGDumpEscapeString(const char *pszStrValue, int nMaxLength,
901
                                const char *pszFieldName)
902
238k
{
903
238k
    CPLString osCommand;
904
905
    /* We need to quote and escape string fields. */
906
238k
    osCommand += '\'';
907
908
238k
    size_t nBytesLen = strlen(pszStrValue);
909
238k
    if (nMaxLength > 0 &&
910
802
        CPLStrlenUTF8Ex(pszStrValue) > static_cast<size_t>(nMaxLength))
911
610
    {
912
610
        CPLDebug("PG", "Truncated %s field value, it was too long.",
913
610
                 pszFieldName);
914
915
610
        size_t iUTF8Char = 0;
916
1.33k
        for (size_t iChar = 0; pszStrValue[iChar]; iChar++)
917
1.33k
        {
918
1.33k
            if ((reinterpret_cast<const unsigned char *>(pszStrValue)[iChar] &
919
1.33k
                 0xc0) != 0x80)
920
1.22k
            {
921
1.22k
                if (iUTF8Char == static_cast<size_t>(nMaxLength))
922
610
                {
923
610
                    nBytesLen = iChar;
924
610
                    break;
925
610
                }
926
610
                iUTF8Char++;
927
610
            }
928
1.33k
        }
929
610
    }
930
931
6.42M
    for (size_t i = 0; i < nBytesLen; i++)
932
6.18M
    {
933
6.18M
        if (pszStrValue[i] == '\'')
934
2.41k
        {
935
2.41k
            osCommand += '\'';
936
2.41k
            osCommand += '\'';
937
2.41k
        }
938
6.18M
        else
939
6.18M
        {
940
6.18M
            osCommand += pszStrValue[i];
941
6.18M
        }
942
6.18M
    }
943
944
238k
    osCommand += '\'';
945
946
238k
    return osCommand;
947
238k
}
948
949
/************************************************************************/
950
/*                     OGRPGDumpEscapeStringList( )                     */
951
/************************************************************************/
952
953
static CPLString
954
OGRPGDumpEscapeStringList(char **papszItems, bool bForInsertOrUpdate,
955
                          OGRPGCommonEscapeStringCbk pfnEscapeString,
956
                          void *userdata)
957
0
{
958
0
    bool bFirstItem = true;
959
0
    CPLString osStr;
960
0
    if (bForInsertOrUpdate)
961
0
        osStr += "ARRAY[";
962
0
    else
963
0
        osStr += "{";
964
0
    while (papszItems && *papszItems)
965
0
    {
966
0
        if (!bFirstItem)
967
0
        {
968
0
            osStr += ',';
969
0
        }
970
971
0
        char *pszStr = *papszItems;
972
0
        if (*pszStr != '\0')
973
0
        {
974
0
            if (bForInsertOrUpdate)
975
0
                osStr += pfnEscapeString(userdata, pszStr, 0, "", "");
976
0
            else
977
0
            {
978
0
                osStr += '"';
979
980
0
                while (*pszStr)
981
0
                {
982
0
                    if (*pszStr == '"' || *pszStr == '\\')
983
0
                        osStr += "\\";
984
0
                    osStr += *pszStr;
985
0
                    pszStr++;
986
0
                }
987
988
0
                osStr += '"';
989
0
            }
990
0
        }
991
0
        else
992
0
            osStr += "NULL";
993
994
0
        bFirstItem = false;
995
996
0
        papszItems++;
997
0
    }
998
0
    if (bForInsertOrUpdate)
999
0
    {
1000
0
        osStr += "]";
1001
0
        if (papszItems == nullptr)
1002
0
            osStr += "::varchar[]";
1003
0
    }
1004
0
    else
1005
0
        osStr += "}";
1006
0
    return osStr;
1007
0
}
1008
1009
/************************************************************************/
1010
/*                          AppendFieldValue()                          */
1011
/*                                                                      */
1012
/* Used by CreateFeatureViaInsert() and SetFeature() to format a        */
1013
/* non-empty field value                                                */
1014
/************************************************************************/
1015
1016
void OGRPGCommonAppendFieldValue(CPLString &osCommand, OGRFeature *poFeature,
1017
                                 int i,
1018
                                 OGRPGCommonEscapeStringCbk pfnEscapeString,
1019
                                 void *userdata)
1020
229k
{
1021
229k
    if (poFeature->IsFieldNull(i))
1022
0
    {
1023
0
        osCommand += "NULL";
1024
0
        return;
1025
0
    }
1026
1027
229k
    const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
1028
229k
    OGRFieldType nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
1029
229k
    OGRFieldSubType eSubType = poFeatureDefn->GetFieldDefn(i)->GetSubType();
1030
1031
    // We need special formatting for integer list values.
1032
229k
    if (nOGRFieldType == OFTIntegerList)
1033
0
    {
1034
0
        int nCount, nOff = 0, j;
1035
0
        const int *panItems = poFeature->GetFieldAsIntegerList(i, &nCount);
1036
1037
0
        const size_t nLen = nCount * 13 + 10;
1038
0
        char *pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
1039
0
        strcpy(pszNeedToFree, "'{");
1040
0
        for (j = 0; j < nCount; j++)
1041
0
        {
1042
0
            if (j != 0)
1043
0
                strcat(pszNeedToFree + nOff, ",");
1044
1045
0
            nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
1046
0
            snprintf(pszNeedToFree + nOff, nLen - nOff, "%d", panItems[j]);
1047
0
        }
1048
0
        strcat(pszNeedToFree + nOff, "}'");
1049
1050
0
        osCommand += pszNeedToFree;
1051
0
        CPLFree(pszNeedToFree);
1052
1053
0
        return;
1054
0
    }
1055
1056
229k
    else if (nOGRFieldType == OFTInteger64List)
1057
0
    {
1058
0
        int nCount, nOff = 0, j;
1059
0
        const GIntBig *panItems =
1060
0
            poFeature->GetFieldAsInteger64List(i, &nCount);
1061
1062
0
        const size_t nLen = nCount * 26 + 10;
1063
0
        char *pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
1064
0
        strcpy(pszNeedToFree, "'{");
1065
0
        for (j = 0; j < nCount; j++)
1066
0
        {
1067
0
            if (j != 0)
1068
0
                strcat(pszNeedToFree + nOff, ",");
1069
1070
0
            nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
1071
0
            snprintf(pszNeedToFree + nOff, nLen - nOff, CPL_FRMT_GIB,
1072
0
                     panItems[j]);
1073
0
        }
1074
0
        strcat(pszNeedToFree + nOff, "}'");
1075
1076
0
        osCommand += pszNeedToFree;
1077
0
        CPLFree(pszNeedToFree);
1078
1079
0
        return;
1080
0
    }
1081
1082
    // We need special formatting for real list values.
1083
229k
    else if (nOGRFieldType == OFTRealList)
1084
0
    {
1085
0
        int nCount = 0;
1086
0
        int nOff = 0;
1087
0
        const double *padfItems = poFeature->GetFieldAsDoubleList(i, &nCount);
1088
1089
0
        const size_t nLen = nCount * 40 + 10;
1090
0
        char *pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
1091
0
        strcpy(pszNeedToFree, "'{");
1092
0
        for (int j = 0; j < nCount; j++)
1093
0
        {
1094
0
            if (j != 0)
1095
0
                strcat(pszNeedToFree + nOff, ",");
1096
1097
0
            nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
1098
            // Check for special values. They need to be quoted.
1099
0
            if (std::isnan(padfItems[j]))
1100
0
                snprintf(pszNeedToFree + nOff, nLen - nOff, "NaN");
1101
0
            else if (std::isinf(padfItems[j]))
1102
0
                snprintf(pszNeedToFree + nOff, nLen - nOff,
1103
0
                         (padfItems[j] > 0) ? "Infinity" : "-Infinity");
1104
0
            else
1105
0
                CPLsnprintf(pszNeedToFree + nOff, nLen - nOff, "%.16g",
1106
0
                            padfItems[j]);
1107
0
        }
1108
0
        strcat(pszNeedToFree + nOff, "}'");
1109
1110
0
        osCommand += pszNeedToFree;
1111
0
        CPLFree(pszNeedToFree);
1112
1113
0
        return;
1114
0
    }
1115
1116
    // We need special formatting for string list values.
1117
229k
    else if (nOGRFieldType == OFTStringList)
1118
0
    {
1119
0
        char **papszItems = poFeature->GetFieldAsStringList(i);
1120
1121
0
        osCommand += OGRPGDumpEscapeStringList(papszItems, true,
1122
0
                                               pfnEscapeString, userdata);
1123
1124
0
        return;
1125
0
    }
1126
1127
    // Binary formatting
1128
229k
    else if (nOGRFieldType == OFTBinary)
1129
0
    {
1130
0
        osCommand += "E'";
1131
1132
0
        int nLen = 0;
1133
0
        GByte *pabyData = poFeature->GetFieldAsBinary(i, &nLen);
1134
0
        char *pszBytea = OGRPGCommonGByteArrayToBYTEA(pabyData, nLen);
1135
1136
0
        osCommand += pszBytea;
1137
1138
0
        CPLFree(pszBytea);
1139
0
        osCommand += "'";
1140
1141
0
        return;
1142
0
    }
1143
1144
    // Flag indicating NULL or not-a-date date value
1145
    // e.g. 0000-00-00 - there is no year 0
1146
229k
    bool bIsDateNull = false;
1147
1148
229k
    const char *pszStrValue = poFeature->GetFieldAsString(i);
1149
1150
    // Check if date is NULL: 0000-00-00
1151
229k
    if (nOGRFieldType == OFTDate)
1152
0
    {
1153
0
        if (STARTS_WITH_CI(pszStrValue, "0000"))
1154
0
        {
1155
0
            pszStrValue = "NULL";
1156
0
            bIsDateNull = true;
1157
0
        }
1158
0
    }
1159
229k
    else if (nOGRFieldType == OFTReal)
1160
118
    {
1161
        // Check for special values. They need to be quoted.
1162
118
        double dfVal = poFeature->GetFieldAsDouble(i);
1163
118
        if (std::isnan(dfVal))
1164
0
            pszStrValue = "'NaN'";
1165
118
        else if (std::isinf(dfVal))
1166
0
            pszStrValue = (dfVal > 0) ? "'Infinity'" : "'-Infinity'";
1167
118
    }
1168
229k
    else if ((nOGRFieldType == OFTInteger || nOGRFieldType == OFTInteger64) &&
1169
191
             eSubType == OFSTBoolean)
1170
0
        pszStrValue = poFeature->GetFieldAsInteger(i) ? "'t'" : "'f'";
1171
1172
229k
    if (nOGRFieldType != OFTInteger && nOGRFieldType != OFTInteger64 &&
1173
229k
        nOGRFieldType != OFTReal && nOGRFieldType != OFTStringList &&
1174
229k
        !bIsDateNull)
1175
229k
    {
1176
229k
        osCommand += pfnEscapeString(
1177
229k
            userdata, pszStrValue, poFeatureDefn->GetFieldDefn(i)->GetWidth(),
1178
229k
            poFeatureDefn->GetName(),
1179
229k
            poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1180
229k
    }
1181
309
    else
1182
309
    {
1183
309
        osCommand += pszStrValue;
1184
309
    }
1185
229k
}
1186
1187
/************************************************************************/
1188
/*                    OGRPGCommonGByteArrayToBYTEA()                    */
1189
/************************************************************************/
1190
1191
char *OGRPGCommonGByteArrayToBYTEA(const GByte *pabyData, size_t nLen)
1192
0
{
1193
0
    if (nLen > (std::numeric_limits<size_t>::max() - 1) / 5)
1194
0
    {
1195
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too big byte array");
1196
0
        return CPLStrdup("");
1197
0
    }
1198
0
    const size_t nTextBufLen = nLen * 5 + 1;
1199
0
    char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextBufLen));
1200
0
    if (pszTextBuf == nullptr)
1201
0
        return CPLStrdup("");
1202
1203
0
    size_t iDst = 0;
1204
1205
0
    for (size_t iSrc = 0; iSrc < nLen; iSrc++)
1206
0
    {
1207
0
        if (pabyData[iSrc] < 40 || pabyData[iSrc] > 126 ||
1208
0
            pabyData[iSrc] == '\\')
1209
0
        {
1210
0
            snprintf(pszTextBuf + iDst, nTextBufLen - iDst, "\\\\%03o",
1211
0
                     pabyData[iSrc]);
1212
0
            iDst += 5;
1213
0
        }
1214
0
        else
1215
0
            pszTextBuf[iDst++] = pabyData[iSrc];
1216
0
    }
1217
0
    pszTextBuf[iDst] = '\0';
1218
1219
0
    return pszTextBuf;
1220
0
}
1221
1222
/************************************************************************/
1223
/*                      OGRPGCommonLayerGetType()                       */
1224
/************************************************************************/
1225
1226
CPLString OGRPGCommonLayerGetType(const OGRFieldDefn &oField,
1227
                                  bool bPreservePrecision, bool bApproxOK)
1228
34.1k
{
1229
34.1k
    const char *pszFieldType = "";
1230
1231
    /* -------------------------------------------------------------------- */
1232
    /*      Work out the PostgreSQL type.                                   */
1233
    /* -------------------------------------------------------------------- */
1234
34.1k
    if (oField.GetType() == OFTInteger)
1235
30
    {
1236
30
        if (oField.GetSubType() == OFSTBoolean)
1237
0
            pszFieldType = "BOOLEAN";
1238
30
        else if (oField.GetSubType() == OFSTInt16)
1239
0
            pszFieldType = "SMALLINT";
1240
30
        else if (oField.GetWidth() > 0 && bPreservePrecision)
1241
0
            pszFieldType = CPLSPrintf("NUMERIC(%d,0)", oField.GetWidth());
1242
30
        else
1243
30
            pszFieldType = "INTEGER";
1244
30
    }
1245
34.1k
    else if (oField.GetType() == OFTInteger64)
1246
1
    {
1247
1
        if (oField.GetWidth() > 0 && bPreservePrecision)
1248
0
            pszFieldType = CPLSPrintf("NUMERIC(%d,0)", oField.GetWidth());
1249
1
        else
1250
1
            pszFieldType = "INT8";
1251
1
    }
1252
34.1k
    else if (oField.GetType() == OFTReal)
1253
23
    {
1254
23
        if (oField.GetSubType() == OFSTFloat32)
1255
0
            pszFieldType = "REAL";
1256
23
        else if (oField.GetWidth() > 0 && oField.GetPrecision() > 0 &&
1257
0
                 bPreservePrecision)
1258
0
            pszFieldType = CPLSPrintf("NUMERIC(%d,%d)", oField.GetWidth(),
1259
0
                                      oField.GetPrecision());
1260
23
        else
1261
23
            pszFieldType = "FLOAT8";
1262
23
    }
1263
34.1k
    else if (oField.GetType() == OFTString)
1264
34.1k
    {
1265
34.1k
        if (oField.GetSubType() == OFSTJSON)
1266
0
            pszFieldType = CPLGetConfigOption("OGR_PG_JSON_TYPE", "JSON");
1267
34.1k
        else if (oField.GetSubType() == OFSTUUID)
1268
0
            pszFieldType = CPLGetConfigOption("OGR_PG_UUID_TYPE", "UUID");
1269
34.1k
        else if (oField.GetWidth() > 0 && oField.GetWidth() < 10485760 &&
1270
11
                 bPreservePrecision)
1271
11
            pszFieldType = CPLSPrintf("VARCHAR(%d)", oField.GetWidth());
1272
34.0k
        else
1273
34.0k
            pszFieldType = CPLGetConfigOption("OGR_PG_STRING_TYPE", "VARCHAR");
1274
34.1k
    }
1275
0
    else if (oField.GetType() == OFTIntegerList)
1276
0
    {
1277
0
        if (oField.GetSubType() == OFSTBoolean)
1278
0
            pszFieldType = "BOOLEAN[]";
1279
0
        else if (oField.GetSubType() == OFSTInt16)
1280
0
            pszFieldType = "INT2[]";
1281
0
        else
1282
0
            pszFieldType = "INTEGER[]";
1283
0
    }
1284
0
    else if (oField.GetType() == OFTInteger64List)
1285
0
    {
1286
0
        pszFieldType = "INT8[]";
1287
0
    }
1288
0
    else if (oField.GetType() == OFTRealList)
1289
0
    {
1290
0
        if (oField.GetSubType() == OFSTFloat32)
1291
0
            pszFieldType = "REAL[]";
1292
0
        else
1293
0
            pszFieldType = "FLOAT8[]";
1294
0
    }
1295
0
    else if (oField.GetType() == OFTStringList)
1296
0
    {
1297
0
        pszFieldType = "varchar[]";
1298
0
    }
1299
0
    else if (oField.GetType() == OFTDate)
1300
0
    {
1301
0
        pszFieldType = "date";
1302
0
    }
1303
0
    else if (oField.GetType() == OFTTime)
1304
0
    {
1305
0
        pszFieldType = "time";
1306
0
    }
1307
0
    else if (oField.GetType() == OFTDateTime)
1308
0
    {
1309
0
        pszFieldType = "timestamp with time zone";
1310
0
    }
1311
0
    else if (oField.GetType() == OFTBinary)
1312
0
    {
1313
0
        pszFieldType = "bytea";
1314
0
    }
1315
0
    else if (bApproxOK)
1316
0
    {
1317
0
        CPLError(CE_Warning, CPLE_NotSupported,
1318
0
                 "Can't create field %s with type %s on PostgreSQL layers.  "
1319
0
                 "Creating as VARCHAR.",
1320
0
                 oField.GetNameRef(),
1321
0
                 OGRFieldDefn::GetFieldTypeName(oField.GetType()));
1322
0
        pszFieldType = "VARCHAR";
1323
0
    }
1324
0
    else
1325
0
    {
1326
0
        CPLError(CE_Failure, CPLE_NotSupported,
1327
0
                 "Can't create field %s with type %s on PostgreSQL layers.",
1328
0
                 oField.GetNameRef(),
1329
0
                 OGRFieldDefn::GetFieldTypeName(oField.GetType()));
1330
0
    }
1331
1332
34.1k
    return pszFieldType;
1333
34.1k
}
1334
1335
/************************************************************************/
1336
/*                      OGRPGCommonLayerSetType()                       */
1337
/************************************************************************/
1338
1339
bool OGRPGCommonLayerSetType(OGRFieldDefn &oField, const char *pszType,
1340
                             const char *pszFormatType, int nWidth)
1341
0
{
1342
0
    if (EQUAL(pszType, "text"))
1343
0
    {
1344
0
        oField.SetType(OFTString);
1345
0
    }
1346
0
    else if (EQUAL(pszType, "_bpchar") || EQUAL(pszType, "_varchar") ||
1347
0
             EQUAL(pszType, "_text"))
1348
0
    {
1349
0
        oField.SetType(OFTStringList);
1350
0
    }
1351
0
    else if (EQUAL(pszType, "bpchar") || EQUAL(pszType, "varchar"))
1352
0
    {
1353
0
        if (nWidth == -1)
1354
0
        {
1355
0
            if (STARTS_WITH_CI(pszFormatType, "character("))
1356
0
                nWidth = atoi(pszFormatType + 10);
1357
0
            else if (STARTS_WITH_CI(pszFormatType, "character varying("))
1358
0
                nWidth = atoi(pszFormatType + 18);
1359
0
            else
1360
0
                nWidth = 0;
1361
0
        }
1362
0
        oField.SetType(OFTString);
1363
0
        oField.SetWidth(nWidth);
1364
0
    }
1365
0
    else if (EQUAL(pszType, "bool"))
1366
0
    {
1367
0
        oField.SetType(OFTInteger);
1368
0
        oField.SetSubType(OFSTBoolean);
1369
0
        oField.SetWidth(1);
1370
0
    }
1371
0
    else if (EQUAL(pszType, "_numeric"))
1372
0
    {
1373
0
        if (EQUAL(pszFormatType, "numeric[]"))
1374
0
            oField.SetType(OFTRealList);
1375
0
        else
1376
0
        {
1377
0
            const char *pszPrecision = strstr(pszFormatType, ",");
1378
0
            int nPrecision = 0;
1379
1380
0
            nWidth = atoi(pszFormatType + 8);
1381
0
            if (pszPrecision != nullptr)
1382
0
                nPrecision = atoi(pszPrecision + 1);
1383
1384
0
            if (nPrecision == 0)
1385
0
            {
1386
0
                if (nWidth >= 10)
1387
0
                    oField.SetType(OFTInteger64List);
1388
0
                else
1389
0
                    oField.SetType(OFTIntegerList);
1390
0
            }
1391
0
            else
1392
0
                oField.SetType(OFTRealList);
1393
1394
0
            oField.SetWidth(nWidth);
1395
0
            oField.SetPrecision(nPrecision);
1396
0
        }
1397
0
    }
1398
0
    else if (EQUAL(pszType, "numeric"))
1399
0
    {
1400
0
        if (EQUAL(pszFormatType, "numeric"))
1401
0
            oField.SetType(OFTReal);
1402
0
        else
1403
0
        {
1404
0
            const char *pszPrecision = strstr(pszFormatType, ",");
1405
0
            int nPrecision = 0;
1406
1407
0
            nWidth = atoi(pszFormatType + 8);
1408
0
            if (pszPrecision != nullptr)
1409
0
                nPrecision = atoi(pszPrecision + 1);
1410
1411
0
            if (nPrecision == 0)
1412
0
            {
1413
0
                if (nWidth >= 10)
1414
0
                    oField.SetType(OFTInteger64);
1415
0
                else
1416
0
                    oField.SetType(OFTInteger);
1417
0
            }
1418
0
            else
1419
0
                oField.SetType(OFTReal);
1420
1421
0
            oField.SetWidth(nWidth);
1422
0
            oField.SetPrecision(nPrecision);
1423
0
        }
1424
0
    }
1425
0
    else if (EQUAL(pszFormatType, "integer[]"))
1426
0
    {
1427
0
        oField.SetType(OFTIntegerList);
1428
0
    }
1429
0
    else if (EQUAL(pszFormatType, "smallint[]"))
1430
0
    {
1431
0
        oField.SetType(OFTIntegerList);
1432
0
        oField.SetSubType(OFSTInt16);
1433
0
    }
1434
0
    else if (EQUAL(pszFormatType, "boolean[]"))
1435
0
    {
1436
0
        oField.SetType(OFTIntegerList);
1437
0
        oField.SetSubType(OFSTBoolean);
1438
0
    }
1439
0
    else if (EQUAL(pszFormatType, "float[]") || EQUAL(pszFormatType, "real[]"))
1440
0
    {
1441
0
        oField.SetType(OFTRealList);
1442
0
        oField.SetSubType(OFSTFloat32);
1443
0
    }
1444
0
    else if (EQUAL(pszFormatType, "double precision[]"))
1445
0
    {
1446
0
        oField.SetType(OFTRealList);
1447
0
    }
1448
0
    else if (EQUAL(pszType, "int2"))
1449
0
    {
1450
0
        oField.SetType(OFTInteger);
1451
0
        oField.SetSubType(OFSTInt16);
1452
0
        oField.SetWidth(5);
1453
0
    }
1454
0
    else if (EQUAL(pszType, "int8"))
1455
0
    {
1456
0
        oField.SetType(OFTInteger64);
1457
0
    }
1458
0
    else if (EQUAL(pszFormatType, "bigint[]"))
1459
0
    {
1460
0
        oField.SetType(OFTInteger64List);
1461
0
    }
1462
0
    else if (STARTS_WITH_CI(pszType, "int"))
1463
0
    {
1464
0
        oField.SetType(OFTInteger);
1465
0
    }
1466
0
    else if (EQUAL(pszType, "float4"))
1467
0
    {
1468
0
        oField.SetType(OFTReal);
1469
0
        oField.SetSubType(OFSTFloat32);
1470
0
    }
1471
0
    else if (STARTS_WITH_CI(pszType, "float") ||
1472
0
             STARTS_WITH_CI(pszType, "double") || EQUAL(pszType, "real"))
1473
0
    {
1474
0
        oField.SetType(OFTReal);
1475
0
    }
1476
0
    else if (STARTS_WITH_CI(pszType, "timestamp"))
1477
0
    {
1478
0
        oField.SetType(OFTDateTime);
1479
0
    }
1480
0
    else if (STARTS_WITH_CI(pszType, "date"))
1481
0
    {
1482
0
        oField.SetType(OFTDate);
1483
0
    }
1484
0
    else if (STARTS_WITH_CI(pszType, "time"))
1485
0
    {
1486
0
        oField.SetType(OFTTime);
1487
0
    }
1488
0
    else if (EQUAL(pszType, "bytea"))
1489
0
    {
1490
0
        oField.SetType(OFTBinary);
1491
0
    }
1492
0
    else if (EQUAL(pszType, "json") || EQUAL(pszType, "jsonb"))
1493
0
    {
1494
0
        oField.SetType(OFTString);
1495
0
        oField.SetSubType(OFSTJSON);
1496
0
    }
1497
0
    else if (EQUAL(pszType, "uuid"))
1498
0
    {
1499
0
        oField.SetType(OFTString);
1500
0
        oField.SetSubType(OFSTUUID);
1501
0
    }
1502
0
    else
1503
0
    {
1504
0
        CPLDebug("PGCommon", "Field %s is of unknown format type %s (type=%s).",
1505
0
                 oField.GetNameRef(), pszFormatType, pszType);
1506
0
        return false;
1507
0
    }
1508
0
    return true;
1509
0
}
1510
1511
/************************************************************************/
1512
/*                  OGRPGCommonLayerNormalizeDefault()                  */
1513
/************************************************************************/
1514
1515
void OGRPGCommonLayerNormalizeDefault(OGRFieldDefn *poFieldDefn,
1516
                                      const char *pszDefault)
1517
0
{
1518
0
    if (pszDefault == nullptr)
1519
0
        return;
1520
0
    CPLString osDefault(pszDefault);
1521
0
    size_t nPos = osDefault.find("::character varying");
1522
0
    if (nPos != std::string::npos &&
1523
0
        nPos + strlen("::character varying") == osDefault.size())
1524
0
    {
1525
0
        osDefault.resize(nPos);
1526
0
    }
1527
0
    else if ((nPos = osDefault.find("::text")) != std::string::npos &&
1528
0
             nPos + strlen("::text") == osDefault.size())
1529
0
    {
1530
0
        osDefault.resize(nPos);
1531
0
    }
1532
0
    else if (strcmp(osDefault, "now()") == 0)
1533
0
        osDefault = "CURRENT_TIMESTAMP";
1534
0
    else if (strcmp(osDefault, "('now'::text)::date") == 0)
1535
0
        osDefault = "CURRENT_DATE";
1536
0
    else if (strcmp(osDefault, "('now'::text)::time with time zone") == 0)
1537
0
        osDefault = "CURRENT_TIME";
1538
0
    else
1539
0
    {
1540
0
        nPos = osDefault.find("::timestamp with time zone");
1541
0
        if (poFieldDefn->GetType() == OFTDateTime && nPos != std::string::npos)
1542
0
        {
1543
0
            osDefault.resize(nPos);
1544
0
            nPos = osDefault.find("'+");
1545
0
            if (nPos != std::string::npos)
1546
0
            {
1547
0
                osDefault.resize(nPos);
1548
0
                osDefault += "'";
1549
0
            }
1550
0
            int nYear = 0;
1551
0
            int nMonth = 0;
1552
0
            int nDay = 0;
1553
0
            int nHour = 0;
1554
0
            int nMinute = 0;
1555
0
            float fSecond = 0.0f;
1556
0
            if (sscanf(osDefault, "'%d-%d-%d %d:%d:%f'", &nYear, &nMonth, &nDay,
1557
0
                       &nHour, &nMinute, &fSecond) == 6 ||
1558
0
                sscanf(osDefault, "'%d-%d-%d %d:%d:%f+00'", &nYear, &nMonth,
1559
0
                       &nDay, &nHour, &nMinute, &fSecond) == 6)
1560
0
            {
1561
0
                if (osDefault.find('.') == std::string::npos)
1562
0
                    osDefault = CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'",
1563
0
                                           nYear, nMonth, nDay, nHour, nMinute,
1564
0
                                           static_cast<int>(fSecond + 0.5f));
1565
0
                else
1566
0
                    osDefault =
1567
0
                        CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%06.3f'", nYear,
1568
0
                                   nMonth, nDay, nHour, nMinute, fSecond);
1569
0
            }
1570
0
        }
1571
0
    }
1572
0
    poFieldDefn->SetDefault(osDefault);
1573
0
}
1574
1575
/************************************************************************/
1576
/*                    OGRPGCommonLayerGetPGDefault()                    */
1577
/************************************************************************/
1578
1579
CPLString OGRPGCommonLayerGetPGDefault(OGRFieldDefn *poFieldDefn)
1580
0
{
1581
0
    CPLString osRet = poFieldDefn->GetDefault();
1582
0
    int nYear = 0;
1583
0
    int nMonth = 0;
1584
0
    int nDay = 0;
1585
0
    int nHour = 0;
1586
0
    int nMinute = 0;
1587
0
    float fSecond = 0.0f;
1588
0
    if (sscanf(osRet, "'%d/%d/%d %d:%d:%f'", &nYear, &nMonth, &nDay, &nHour,
1589
0
               &nMinute, &fSecond) == 6)
1590
0
    {
1591
0
        osRet.pop_back();
1592
0
        osRet += "+00'::timestamp with time zone";
1593
0
    }
1594
0
    return osRet;
1595
0
}
1596
1597
/************************************************************************/
1598
/*              OGRPGCommonGenerateShortEnoughIdentifier()              */
1599
/************************************************************************/
1600
1601
std::string OGRPGCommonGenerateShortEnoughIdentifier(const char *pszIdentifier)
1602
0
{
1603
0
    if (CPLStrlenUTF8Ex(pszIdentifier) <=
1604
0
        static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
1605
0
        return pszIdentifier;
1606
1607
    // Truncate string by making sure we don't cut in the
1608
    // middle of a UTF-8 multibyte character
1609
    // Continuation bytes of such characters are of the form
1610
    // 10xxxxxx (0x80), whereas single-byte are 0xxxxxxx
1611
    // and the start of a multi-byte is 11xxxxxx
1612
0
    std::string osRet;
1613
0
    constexpr int FIRST_8_CHARS_OF_MD5 = 8;
1614
0
    int iUTF8Char = 0;
1615
0
    for (size_t i = 0; pszIdentifier[i]; ++i)
1616
0
    {
1617
0
        if ((pszIdentifier[i] & 0xc0) != 0x80)
1618
0
        {
1619
0
            ++iUTF8Char;
1620
0
            if (iUTF8Char == OGR_PG_NAMEDATALEN - 1 - FIRST_8_CHARS_OF_MD5)
1621
0
                break;
1622
0
        }
1623
0
        osRet += pszIdentifier[i];
1624
0
    }
1625
0
    osRet += '_';
1626
0
    osRet += std::string(CPLMD5String(pszIdentifier), FIRST_8_CHARS_OF_MD5);
1627
0
    return osRet;
1628
0
}
1629
1630
/************************************************************************/
1631
/*                OGRPGCommonGenerateSpatialIndexName()                 */
1632
/************************************************************************/
1633
1634
/** Generates the name of the spatial index on table pszTableName
1635
 * using pszGeomFieldName, such that it fits in OGR_PG_NAMEDATALEN - 1 bytes.
1636
 * The index of the geometry field may be used if the geometry field name
1637
 * is too long.
1638
 */
1639
std::string OGRPGCommonGenerateSpatialIndexName(const char *pszTableName,
1640
                                                const char *pszGeomFieldName,
1641
                                                int nGeomFieldIdx)
1642
2.77k
{
1643
    // Nominal case: use full table and geometry field name
1644
2.77k
    for (const char *pszSuffix : {"_geom_idx", "_idx"})
1645
3.78k
    {
1646
3.78k
        if (CPLStrlenUTF8Ex(pszTableName) + 1 +
1647
3.78k
                CPLStrlenUTF8Ex(pszGeomFieldName) + strlen(pszSuffix) <=
1648
3.78k
            static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
1649
2.01k
        {
1650
2.01k
            std::string osRet(pszTableName);
1651
2.01k
            osRet += '_';
1652
2.01k
            osRet += pszGeomFieldName;
1653
2.01k
            osRet += pszSuffix;
1654
2.01k
            return osRet;
1655
2.01k
        }
1656
3.78k
    }
1657
1658
    // Slightly degraded case: use table name and geometry field index
1659
755
    const std::string osGeomFieldIdx(CPLSPrintf("%d", nGeomFieldIdx));
1660
755
    if (CPLStrlenUTF8Ex(pszTableName) + 1 + osGeomFieldIdx.size() +
1661
755
            strlen("_geom_idx") <=
1662
755
        static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
1663
633
    {
1664
633
        std::string osRet(pszTableName);
1665
633
        osRet += '_';
1666
633
        osRet += osGeomFieldIdx;
1667
633
        osRet += "_geom_idx";
1668
633
        return osRet;
1669
633
    }
1670
1671
    // Fallback case: use first characters of table name,
1672
    // first 8 chars of its MD5 and then the geometry field index.
1673
122
    constexpr int FIRST_8_CHARS_OF_MD5 = 8;
1674
122
    std::string osSuffix("_");
1675
122
    osSuffix += std::string(CPLMD5String(pszTableName), FIRST_8_CHARS_OF_MD5);
1676
122
    osSuffix += '_';
1677
122
    osSuffix += osGeomFieldIdx;
1678
122
    osSuffix += "_geom_idx";
1679
1680
122
    std::string osRet;
1681
122
    size_t iUTF8Char = 0;
1682
5.68k
    for (size_t i = 0; pszTableName[i]; ++i)
1683
5.68k
    {
1684
5.68k
        if ((pszTableName[i] & 0xc0) != 0x80)
1685
5.27k
        {
1686
5.27k
            ++iUTF8Char;
1687
5.27k
            if (iUTF8Char == OGR_PG_NAMEDATALEN - osSuffix.size())
1688
122
                break;
1689
5.27k
        }
1690
5.56k
        osRet += pszTableName[i];
1691
5.56k
    }
1692
122
    osRet += osSuffix;
1693
122
    return osRet;
1694
755
}
1695
1696
/************************************************************************/
1697
/*                           GetNextFeature()                           */
1698
/************************************************************************/
1699
1700
OGRErr OGRPGDumpLayer::CreateField(const OGRFieldDefn *poFieldIn, int bApproxOK)
1701
34.1k
{
1702
34.1k
    if (m_poFeatureDefn->GetFieldCount() +
1703
34.1k
            m_poFeatureDefn->GetGeomFieldCount() ==
1704
34.1k
        1600)
1705
4
    {
1706
4
        CPLError(CE_Failure, CPLE_AppDefined,
1707
4
                 "Maximum number of fields supported is 1600.");
1708
4
        return OGRERR_FAILURE;
1709
4
    }
1710
1711
34.1k
    CPLString osFieldType;
1712
34.1k
    OGRFieldDefn oField(poFieldIn);
1713
1714
    // Can be set to NO to test ogr2ogr default behavior
1715
34.1k
    const bool bAllowCreationOfFieldWithFIDName =
1716
34.1k
        CPLTestBool(CPLGetConfigOption(
1717
34.1k
            "PGDUMP_DEBUG_ALLOW_CREATION_FIELD_WITH_FID_NAME", "YES"));
1718
1719
34.1k
    if (bAllowCreationOfFieldWithFIDName && m_pszFIDColumn != nullptr &&
1720
34.1k
        EQUAL(oField.GetNameRef(), m_pszFIDColumn) &&
1721
0
        oField.GetType() != OFTInteger && oField.GetType() != OFTInteger64)
1722
0
    {
1723
0
        CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
1724
0
                 oField.GetNameRef());
1725
0
        return OGRERR_FAILURE;
1726
0
    }
1727
1728
    /* -------------------------------------------------------------------- */
1729
    /*      Do we want to "launder" the column names into Postgres          */
1730
    /*      friendly format?                                                */
1731
    /* -------------------------------------------------------------------- */
1732
34.1k
    if (m_bLaunderColumnNames)
1733
34.1k
    {
1734
34.1k
        char *pszSafeName = OGRPGCommonLaunderName(oField.GetNameRef(),
1735
34.1k
                                                   "PGDump", m_bUTF8ToASCII);
1736
1737
34.1k
        oField.SetName(pszSafeName);
1738
34.1k
        CPLFree(pszSafeName);
1739
1740
34.1k
        if (EQUAL(oField.GetNameRef(), "oid"))
1741
0
        {
1742
0
            CPLError(CE_Warning, CPLE_AppDefined,
1743
0
                     "Renaming field 'oid' to 'oid_' to avoid conflict with "
1744
0
                     "internal oid field.");
1745
0
            oField.SetName("oid_");
1746
0
        }
1747
34.1k
    }
1748
1749
34.1k
    const char *pszOverrideType =
1750
34.1k
        m_apszOverrideColumnTypes.FetchNameValue(oField.GetNameRef());
1751
34.1k
    if (pszOverrideType != nullptr)
1752
0
    {
1753
0
        osFieldType = pszOverrideType;
1754
0
    }
1755
34.1k
    else
1756
34.1k
    {
1757
34.1k
        osFieldType = OGRPGCommonLayerGetType(oField, m_bPreservePrecision,
1758
34.1k
                                              CPL_TO_BOOL(bApproxOK));
1759
34.1k
        if (osFieldType.empty())
1760
0
            return OGRERR_FAILURE;
1761
34.1k
    }
1762
1763
    /* -------------------------------------------------------------------- */
1764
    /*      Create the new field.                                           */
1765
    /* -------------------------------------------------------------------- */
1766
34.1k
    CPLString osCommand;
1767
34.1k
    osCommand.Printf("ALTER TABLE %s ADD COLUMN %s %s", m_pszSqlTableName,
1768
34.1k
                     OGRPGDumpEscapeColumnName(oField.GetNameRef()).c_str(),
1769
34.1k
                     osFieldType.c_str());
1770
34.1k
    if (!oField.IsNullable())
1771
0
        osCommand += " NOT NULL";
1772
34.1k
    if (oField.IsUnique())
1773
0
        osCommand += " UNIQUE";
1774
34.1k
    if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
1775
0
    {
1776
0
        osCommand += " DEFAULT ";
1777
0
        osCommand += OGRPGCommonLayerGetPGDefault(&oField);
1778
0
    }
1779
1780
34.1k
    m_poFeatureDefn->AddFieldDefn(&oField);
1781
1782
34.1k
    if (bAllowCreationOfFieldWithFIDName && m_pszFIDColumn != nullptr &&
1783
34.1k
        EQUAL(oField.GetNameRef(), m_pszFIDColumn))
1784
0
    {
1785
0
        m_iFIDAsRegularColumnIndex = m_poFeatureDefn->GetFieldCount() - 1;
1786
0
    }
1787
34.1k
    else if (m_bCreateTable)
1788
34.1k
    {
1789
34.1k
        const auto Log = [this](const std::string &osSQL)
1790
34.1k
        {
1791
34.1k
            if (m_bGeomColumnPositionImmediate)
1792
34.1k
                m_poDS->Log(osSQL.c_str());
1793
0
            else
1794
0
                m_aosDeferrentNonGeomFieldCreationCommands.push_back(osSQL);
1795
34.1k
        };
1796
1797
34.1k
        Log(osCommand);
1798
1799
34.1k
        if (!oField.GetComment().empty())
1800
0
        {
1801
0
            std::string osCommentON;
1802
0
            osCommentON = "COMMENT ON COLUMN ";
1803
0
            osCommentON += m_pszSqlTableName;
1804
0
            osCommentON += '.';
1805
0
            osCommentON += OGRPGDumpEscapeColumnName(oField.GetNameRef());
1806
0
            osCommentON += " IS ";
1807
0
            osCommentON += OGRPGDumpEscapeString(oField.GetComment().c_str());
1808
0
            Log(osCommentON);
1809
0
        }
1810
34.1k
    }
1811
1812
34.1k
    return OGRERR_NONE;
1813
34.1k
}
1814
1815
/************************************************************************/
1816
/*                          CreateGeomField()                           */
1817
/************************************************************************/
1818
1819
OGRErr OGRPGDumpLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
1820
                                       int /* bApproxOK */)
1821
2.58k
{
1822
2.58k
    if (m_poFeatureDefn->GetFieldCount() +
1823
2.58k
            m_poFeatureDefn->GetGeomFieldCount() ==
1824
2.58k
        1600)
1825
0
    {
1826
0
        CPLError(CE_Failure, CPLE_AppDefined,
1827
0
                 "Maximum number of fields supported is 1600.");
1828
0
        return OGRERR_FAILURE;
1829
0
    }
1830
1831
2.58k
    OGRwkbGeometryType eType = poGeomFieldIn->GetType();
1832
2.58k
    if (eType == wkbNone)
1833
0
    {
1834
0
        CPLError(CE_Failure, CPLE_AppDefined,
1835
0
                 "Cannot create geometry field of type wkbNone");
1836
0
        return OGRERR_FAILURE;
1837
0
    }
1838
1839
    // Check if GEOMETRY_NAME layer creation option was set, but no initial
1840
    // column was created in ICreateLayer()
1841
2.58k
    const CPLString osGeomFieldName =
1842
2.58k
        !m_osFirstGeometryFieldName.empty()
1843
2.58k
            ? m_osFirstGeometryFieldName
1844
2.58k
            : CPLString(poGeomFieldIn->GetNameRef());
1845
1846
2.58k
    m_osFirstGeometryFieldName = "";  // reset for potential next geom columns
1847
1848
2.58k
    OGRGeomFieldDefn oTmpGeomFieldDefn(poGeomFieldIn);
1849
2.58k
    oTmpGeomFieldDefn.SetName(osGeomFieldName);
1850
1851
2.58k
    CPLString osCommand;
1852
2.58k
    auto poGeomField =
1853
2.58k
        std::make_unique<OGRPGDumpGeomFieldDefn>(&oTmpGeomFieldDefn);
1854
1855
    /* -------------------------------------------------------------------- */
1856
    /*      Do we want to "launder" the column names into Postgres          */
1857
    /*      friendly format?                                                */
1858
    /* -------------------------------------------------------------------- */
1859
2.58k
    if (m_bLaunderColumnNames)
1860
2.58k
    {
1861
2.58k
        char *pszSafeName = OGRPGCommonLaunderName(poGeomField->GetNameRef(),
1862
2.58k
                                                   "PGDump", m_bUTF8ToASCII);
1863
1864
2.58k
        poGeomField->SetName(pszSafeName);
1865
2.58k
        CPLFree(pszSafeName);
1866
2.58k
    }
1867
1868
2.58k
    const OGRSpatialReference *poSRS = poGeomField->GetSpatialRef();
1869
2.58k
    int nSRSId = m_nUnknownSRSId;
1870
2.58k
    if (m_nForcedSRSId != -2)
1871
0
        nSRSId = m_nForcedSRSId;
1872
2.58k
    else if (poSRS != nullptr)
1873
1.11k
    {
1874
1.11k
        const char *pszAuthorityName = poSRS->GetAuthorityName();
1875
1.11k
        if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
1876
569
        {
1877
            /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */
1878
569
            nSRSId = atoi(poSRS->GetAuthorityCode());
1879
569
        }
1880
548
        else
1881
548
        {
1882
548
            const char *pszGeogCSName = poSRS->GetAttrValue("GEOGCS");
1883
548
            if (pszGeogCSName != nullptr &&
1884
10
                EQUAL(pszGeogCSName, "GCS_WGS_1984"))
1885
0
                nSRSId = 4326;
1886
548
        }
1887
1.11k
    }
1888
1889
2.58k
    poGeomField->m_nSRSId = nSRSId;
1890
1891
2.58k
    int nGeometryTypeFlags = 0;
1892
2.58k
    if (OGR_GT_HasZ(static_cast<OGRwkbGeometryType>(eType)))
1893
12
        nGeometryTypeFlags |= OGRGeometry::OGR_G_3D;
1894
2.58k
    if (OGR_GT_HasM(static_cast<OGRwkbGeometryType>(eType)))
1895
3
        nGeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
1896
2.58k
    if (m_nForcedGeometryTypeFlags >= 0)
1897
0
    {
1898
0
        nGeometryTypeFlags = m_nForcedGeometryTypeFlags;
1899
0
        eType = OGR_GT_SetModifier(
1900
0
            eType, nGeometryTypeFlags & OGRGeometry::OGR_G_3D,
1901
0
            nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED);
1902
0
    }
1903
2.58k
    poGeomField->SetType(eType);
1904
2.58k
    poGeomField->m_nGeometryTypeFlags = nGeometryTypeFlags;
1905
1906
    /* -------------------------------------------------------------------- */
1907
    /*      Create the new field.                                           */
1908
    /* -------------------------------------------------------------------- */
1909
2.58k
    if (m_bCreateTable)
1910
2.58k
    {
1911
2.58k
        const char *suffix = "";
1912
2.58k
        int dim = 2;
1913
2.58k
        if ((poGeomField->m_nGeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
1914
12
            (poGeomField->m_nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
1915
0
            dim = 4;
1916
2.58k
        else if (poGeomField->m_nGeometryTypeFlags &
1917
2.58k
                 OGRGeometry::OGR_G_MEASURED)
1918
3
        {
1919
3
            if (wkbFlatten(poGeomField->GetType()) != wkbUnknown)
1920
0
                suffix = "M";
1921
3
            dim = 3;
1922
3
        }
1923
2.57k
        else if (poGeomField->m_nGeometryTypeFlags & OGRGeometry::OGR_G_3D)
1924
12
            dim = 3;
1925
1926
2.58k
        const char *pszGeometryType = OGRToOGCGeomType(poGeomField->GetType());
1927
2.58k
        osCommand.Printf(
1928
2.58k
            "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
1929
2.58k
            OGRPGDumpEscapeString(m_pszSchemaName).c_str(),
1930
2.58k
            OGRPGDumpEscapeString(m_poFeatureDefn->GetName()).c_str(),
1931
2.58k
            OGRPGDumpEscapeString(poGeomField->GetNameRef()).c_str(), nSRSId,
1932
2.58k
            pszGeometryType, suffix, dim);
1933
1934
2.58k
        if (m_bGeomColumnPositionImmediate)
1935
2.58k
            m_poDS->Log(osCommand);
1936
0
        else
1937
0
            m_aosDeferredGeomFieldCreationCommands.push_back(osCommand);
1938
1939
2.58k
        if (!poGeomField->IsNullable())
1940
0
        {
1941
0
            osCommand.Printf(
1942
0
                "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL",
1943
0
                OGRPGDumpEscapeColumnName(m_poFeatureDefn->GetName()).c_str(),
1944
0
                OGRPGDumpEscapeColumnName(poGeomField->GetNameRef()).c_str());
1945
1946
0
            if (m_bGeomColumnPositionImmediate)
1947
0
                m_poDS->Log(osCommand);
1948
0
            else
1949
0
                m_aosDeferredGeomFieldCreationCommands.push_back(osCommand);
1950
0
        }
1951
1952
2.58k
        if (m_bCreateSpatialIndexFlag)
1953
2.58k
        {
1954
2.58k
            const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
1955
2.58k
                GetName(), poGeomField->GetNameRef(),
1956
2.58k
                m_poFeatureDefn->GetGeomFieldCount()));
1957
1958
2.58k
            osCommand.Printf(
1959
2.58k
                "CREATE INDEX %s ON %s USING %s (%s)",
1960
2.58k
                OGRPGDumpEscapeColumnName(osIndexName.c_str()).c_str(),
1961
2.58k
                m_pszSqlTableName, m_osSpatialIndexType.c_str(),
1962
2.58k
                OGRPGDumpEscapeColumnName(poGeomField->GetNameRef()).c_str());
1963
1964
2.58k
            m_aosSpatialIndexCreationCommands.push_back(std::move(osCommand));
1965
2.58k
        }
1966
2.58k
    }
1967
1968
2.58k
    m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomField));
1969
1970
2.58k
    return OGRERR_NONE;
1971
2.58k
}
1972
1973
/************************************************************************/
1974
/*                       SetOverrideColumnTypes()                       */
1975
/************************************************************************/
1976
1977
void OGRPGDumpLayer::SetOverrideColumnTypes(const char *pszOverrideColumnTypes)
1978
1.09k
{
1979
1.09k
    if (pszOverrideColumnTypes == nullptr)
1980
1.09k
        return;
1981
1982
0
    const char *pszIter = pszOverrideColumnTypes;
1983
0
    std::string osCur;
1984
0
    while (*pszIter != '\0')
1985
0
    {
1986
0
        if (*pszIter == '(')
1987
0
        {
1988
            /* Ignore commas inside ( ) pair */
1989
0
            while (*pszIter != '\0')
1990
0
            {
1991
0
                if (*pszIter == ')')
1992
0
                {
1993
0
                    osCur += *pszIter;
1994
0
                    pszIter++;
1995
0
                    break;
1996
0
                }
1997
0
                osCur += *pszIter;
1998
0
                pszIter++;
1999
0
            }
2000
0
            if (*pszIter == '\0')
2001
0
                break;
2002
0
        }
2003
2004
0
        if (*pszIter == ',')
2005
0
        {
2006
0
            m_apszOverrideColumnTypes.AddString(osCur.c_str());
2007
0
            osCur.clear();
2008
0
        }
2009
0
        else
2010
0
            osCur += *pszIter;
2011
0
        pszIter++;
2012
0
    }
2013
0
    if (!osCur.empty())
2014
0
        m_apszOverrideColumnTypes.AddString(osCur.c_str());
2015
0
}
2016
2017
/************************************************************************/
2018
/*                            SetMetadata()                             */
2019
/************************************************************************/
2020
2021
CPLErr OGRPGDumpLayer::SetMetadata(CSLConstList papszMD, const char *pszDomain)
2022
0
{
2023
0
    OGRLayer::SetMetadata(papszMD, pszDomain);
2024
0
    if (!m_osForcedDescription.empty() &&
2025
0
        (pszDomain == nullptr || EQUAL(pszDomain, "")))
2026
0
    {
2027
0
        OGRLayer::SetMetadataItem("DESCRIPTION", m_osForcedDescription);
2028
0
    }
2029
2030
0
    if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
2031
0
        m_osForcedDescription.empty())
2032
0
    {
2033
0
        const char *l_pszDescription = OGRLayer::GetMetadataItem("DESCRIPTION");
2034
0
        CPLString osCommand;
2035
2036
0
        osCommand.Printf("COMMENT ON TABLE %s IS %s", m_pszSqlTableName,
2037
0
                         l_pszDescription && l_pszDescription[0] != '\0'
2038
0
                             ? OGRPGDumpEscapeString(l_pszDescription).c_str()
2039
0
                             : "NULL");
2040
0
        m_poDS->Log(osCommand);
2041
0
    }
2042
2043
0
    return CE_None;
2044
0
}
2045
2046
/************************************************************************/
2047
/*                          SetMetadataItem()                           */
2048
/************************************************************************/
2049
2050
CPLErr OGRPGDumpLayer::SetMetadataItem(const char *pszName,
2051
                                       const char *pszValue,
2052
                                       const char *pszDomain)
2053
0
{
2054
0
    if ((pszDomain == nullptr || EQUAL(pszDomain, "")) && pszName != nullptr &&
2055
0
        EQUAL(pszName, "DESCRIPTION") && !m_osForcedDescription.empty())
2056
0
    {
2057
0
        return CE_None;
2058
0
    }
2059
0
    OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
2060
0
    if ((pszDomain == nullptr || EQUAL(pszDomain, "")) && pszName != nullptr &&
2061
0
        EQUAL(pszName, "DESCRIPTION"))
2062
0
    {
2063
0
        SetMetadata(GetMetadata());
2064
0
    }
2065
0
    return CE_None;
2066
0
}
2067
2068
/************************************************************************/
2069
/*                        SetForcedDescription()                        */
2070
/************************************************************************/
2071
2072
void OGRPGDumpLayer::SetForcedDescription(const char *pszDescriptionIn)
2073
0
{
2074
0
    m_osForcedDescription = pszDescriptionIn;
2075
0
    OGRLayer::SetMetadataItem("DESCRIPTION", m_osForcedDescription);
2076
2077
0
    if (pszDescriptionIn[0] != '\0')
2078
0
    {
2079
0
        CPLString osCommand;
2080
0
        osCommand.Printf("COMMENT ON TABLE %s IS %s", m_pszSqlTableName,
2081
0
                         OGRPGDumpEscapeString(pszDescriptionIn).c_str());
2082
0
        m_poDS->Log(osCommand);
2083
0
    }
2084
0
}
2085
2086
/************************************************************************/
2087
/*                             GetDataset()                             */
2088
/************************************************************************/
2089
2090
GDALDataset *OGRPGDumpLayer::GetDataset()
2091
0
{
2092
0
    return m_poDS;
2093
0
}