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