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