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