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