Line data Source code
1 : /******************************************************************************
2 :
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRPGTableLayer class, access to an existing table.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2000, Frank Warmerdam
10 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "ogr_pg.h"
16 : #include "cpl_conv.h"
17 : #include "cpl_string.h"
18 : #include "cpl_error.h"
19 : #include "ogr_p.h"
20 :
21 : #include <algorithm>
22 : #include <chrono>
23 : #include <condition_variable>
24 : #include <limits>
25 : #include <mutex>
26 : #include <thread>
27 :
28 : #define PQexec this_is_an_error
29 :
30 : #define UNSUPPORTED_OP_READ_ONLY \
31 : "%s : unsupported operation on a read-only datasource."
32 :
33 978 : void OGRPGFeatureDefn::UnsetLayer()
34 : {
35 978 : const int nGeomFieldCount = GetGeomFieldCount();
36 1560 : for (int i = 0; i < nGeomFieldCount; i++)
37 582 : cpl::down_cast<OGRPGGeomFieldDefn *>(apoGeomFieldDefn[i].get())
38 582 : ->UnsetLayer();
39 978 : }
40 :
41 : /************************************************************************/
42 : /* OGRPGTableFeatureDefn */
43 : /************************************************************************/
44 :
45 : class OGRPGTableFeatureDefn final : public OGRPGFeatureDefn
46 : {
47 : private:
48 : OGRPGTableFeatureDefn(const OGRPGTableFeatureDefn &) = delete;
49 : OGRPGTableFeatureDefn &operator=(const OGRPGTableFeatureDefn &) = delete;
50 :
51 : OGRPGTableLayer *poLayer = nullptr;
52 :
53 : void SolveFields() const;
54 :
55 : public:
56 754 : explicit OGRPGTableFeatureDefn(OGRPGTableLayer *poLayerIn,
57 : const char *pszName = nullptr)
58 754 : : OGRPGFeatureDefn(pszName), poLayer(poLayerIn)
59 : {
60 754 : }
61 :
62 : void UnsetLayer() override;
63 :
64 134008 : int GetFieldCount() const override
65 : {
66 134008 : SolveFields();
67 134008 : return OGRPGFeatureDefn::GetFieldCount();
68 : }
69 :
70 22491 : OGRFieldDefn *GetFieldDefn(int i) override
71 : {
72 22491 : SolveFields();
73 22491 : return OGRPGFeatureDefn::GetFieldDefn(i);
74 : }
75 :
76 64954 : const OGRFieldDefn *GetFieldDefn(int i) const override
77 : {
78 64954 : SolveFields();
79 64954 : return OGRPGFeatureDefn::GetFieldDefn(i);
80 : }
81 :
82 7984 : int GetFieldIndex(const char *pszName) const override
83 : {
84 7984 : SolveFields();
85 7984 : return OGRPGFeatureDefn::GetFieldIndex(pszName);
86 : }
87 :
88 57296 : int GetGeomFieldCount() const override
89 : {
90 57296 : if (poLayer != nullptr && !poLayer->HasGeometryInformation())
91 1204 : SolveFields();
92 57296 : return OGRPGFeatureDefn::GetGeomFieldCount();
93 : }
94 :
95 14026 : OGRPGGeomFieldDefn *GetGeomFieldDefn(int i) override
96 : {
97 14026 : if (poLayer != nullptr && !poLayer->HasGeometryInformation())
98 163 : SolveFields();
99 14026 : return OGRPGFeatureDefn::GetGeomFieldDefn(i);
100 : }
101 :
102 1946 : const OGRPGGeomFieldDefn *GetGeomFieldDefn(int i) const override
103 : {
104 1946 : if (poLayer != nullptr && !poLayer->HasGeometryInformation())
105 0 : SolveFields();
106 1946 : return OGRPGFeatureDefn::GetGeomFieldDefn(i);
107 : }
108 :
109 926 : int GetGeomFieldIndex(const char *pszName) const override
110 : {
111 926 : if (poLayer != nullptr && !poLayer->HasGeometryInformation())
112 0 : SolveFields();
113 926 : return OGRPGFeatureDefn::GetGeomFieldIndex(pszName);
114 : }
115 : };
116 :
117 754 : void OGRPGTableFeatureDefn::UnsetLayer()
118 : {
119 754 : poLayer = nullptr;
120 754 : OGRPGFeatureDefn::UnsetLayer();
121 754 : }
122 :
123 : /************************************************************************/
124 : /* SolveFields() */
125 : /************************************************************************/
126 :
127 230804 : void OGRPGTableFeatureDefn::SolveFields() const
128 : {
129 230804 : if (poLayer == nullptr)
130 0 : return;
131 :
132 230804 : poLayer->ReadTableDefinition();
133 : }
134 :
135 : /************************************************************************/
136 : /* GetFIDColumn() */
137 : /************************************************************************/
138 :
139 86 : const char *OGRPGTableLayer::GetFIDColumn() const
140 :
141 : {
142 86 : const_cast<OGRPGTableLayer *>(this)->ReadTableDefinition();
143 :
144 86 : if (pszFIDColumn != nullptr)
145 84 : return pszFIDColumn;
146 : else
147 2 : return "";
148 : }
149 :
150 : /************************************************************************/
151 : /* OGRPGTableLayer() */
152 : /************************************************************************/
153 :
154 754 : OGRPGTableLayer::OGRPGTableLayer(OGRPGDataSource *poDSIn,
155 : CPLString &osCurrentSchema,
156 : const char *pszTableNameIn,
157 : const char *pszSchemaNameIn,
158 : const char *pszDescriptionIn,
159 754 : const char *pszGeomColForcedIn, int bUpdate)
160 1508 : : bUpdateAccess(bUpdate), pszTableName(CPLStrdup(pszTableNameIn)),
161 754 : pszSchemaName(CPLStrdup(pszSchemaNameIn ? pszSchemaNameIn
162 396 : : osCurrentSchema.c_str())),
163 754 : m_pszTableDescription(pszDescriptionIn ? CPLStrdup(pszDescriptionIn)
164 : : nullptr),
165 : osPrimaryKey(CPLGetConfigOption("PGSQL_OGR_FID", "ogc_fid")),
166 754 : pszGeomColForced(pszGeomColForcedIn ? CPLStrdup(pszGeomColForcedIn)
167 : : nullptr),
168 : // Just in provision for people yelling about broken backward
169 : // compatibility.
170 : bRetrieveFID(
171 754 : CPLTestBool(CPLGetConfigOption("OGR_PG_RETRIEVE_FID", "TRUE"))),
172 : bSkipConflicts(
173 3770 : CPLTestBool(CPLGetConfigOption("OGR_PG_SKIP_CONFLICTS", "FALSE")))
174 : {
175 754 : poDS = poDSIn;
176 754 : pszQueryStatement = nullptr;
177 :
178 : /* -------------------------------------------------------------------- */
179 : /* Build the layer defn name. */
180 : /* -------------------------------------------------------------------- */
181 1508 : CPLString osDefnName;
182 754 : if (pszSchemaNameIn && osCurrentSchema != pszSchemaNameIn)
183 : {
184 53 : osDefnName.Printf("%s.%s", pszSchemaNameIn, pszTableName);
185 159 : pszSqlTableName = CPLStrdup(CPLString().Printf(
186 106 : "%s.%s", OGRPGEscapeColumnName(pszSchemaNameIn).c_str(),
187 159 : OGRPGEscapeColumnName(pszTableName).c_str()));
188 : }
189 : else
190 : {
191 : // no prefix for current_schema in layer name, for backwards
192 : // compatibility.
193 701 : osDefnName = pszTableName;
194 701 : pszSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszTableName));
195 : }
196 754 : if (pszGeomColForced != nullptr)
197 : {
198 7 : osDefnName += "(";
199 7 : osDefnName += pszGeomColForced;
200 7 : osDefnName += ")";
201 : }
202 :
203 754 : poFeatureDefn = new OGRPGTableFeatureDefn(this, osDefnName);
204 754 : SetDescription(poFeatureDefn->GetName());
205 754 : poFeatureDefn->Reference();
206 :
207 : // bSealFields = false because we do lazy resolution of fields
208 754 : poFeatureDefn->Seal(/* bSealFields = */ false);
209 :
210 754 : if (pszDescriptionIn != nullptr && !EQUAL(pszDescriptionIn, ""))
211 : {
212 2 : OGRLayer::SetMetadataItem("DESCRIPTION", pszDescriptionIn);
213 : }
214 754 : }
215 :
216 : //************************************************************************/
217 : /* ~OGRPGTableLayer() */
218 : /************************************************************************/
219 :
220 1508 : OGRPGTableLayer::~OGRPGTableLayer()
221 :
222 : {
223 754 : if (bDeferredCreation)
224 0 : RunDeferredCreationIfNecessary();
225 754 : if (bCopyActive)
226 0 : EndCopy();
227 754 : UpdateSequenceIfNeeded();
228 754 : SerializeMetadata();
229 :
230 754 : CPLFree(pszSqlTableName);
231 754 : CPLFree(pszTableName);
232 754 : CPLFree(pszSqlGeomParentTableName);
233 754 : CPLFree(pszSchemaName);
234 754 : CPLFree(m_pszTableDescription);
235 754 : CPLFree(pszGeomColForced);
236 754 : CSLDestroy(papszOverrideColumnTypes);
237 1508 : }
238 :
239 : /************************************************************************/
240 : /* LoadMetadata() */
241 : /************************************************************************/
242 :
243 123 : void OGRPGTableLayer::LoadMetadata()
244 : {
245 123 : if (m_bMetadataLoaded)
246 105 : return;
247 37 : m_bMetadataLoaded = true;
248 :
249 37 : if (!poDS->HasOgrSystemTablesMetadataTable())
250 19 : return;
251 :
252 18 : PGconn *hPGConn = poDS->GetPGConn();
253 :
254 : const std::string osSQL(
255 : CPLSPrintf("SELECT metadata FROM ogr_system_tables.metadata WHERE "
256 : "schema_name = %s AND table_name = %s",
257 36 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
258 72 : OGRPGEscapeString(hPGConn, pszTableName).c_str()));
259 18 : auto poSqlLyr = poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
260 18 : if (poSqlLyr)
261 : {
262 : auto poFeature =
263 36 : std::unique_ptr<OGRFeature>(poSqlLyr->GetNextFeature());
264 18 : if (poFeature)
265 : {
266 2 : if (poFeature->IsFieldSetAndNotNull(0))
267 : {
268 2 : const char *pszXML = poFeature->GetFieldAsString(0);
269 2 : if (pszXML)
270 : {
271 2 : auto psRoot = CPLParseXMLString(pszXML);
272 2 : if (psRoot)
273 : {
274 2 : oMDMD.XMLInit(psRoot, true);
275 2 : CPLDestroyXMLNode(psRoot);
276 : }
277 : }
278 : }
279 : }
280 18 : poDS->ReleaseResultSet(poSqlLyr);
281 : }
282 : }
283 :
284 : /************************************************************************/
285 : /* SerializeMetadata() */
286 : /************************************************************************/
287 :
288 754 : void OGRPGTableLayer::SerializeMetadata()
289 : {
290 771 : if (!m_bMetadataModified ||
291 17 : !CPLTestBool(CPLGetConfigOption("OGR_PG_ENABLE_METADATA", "YES")))
292 : {
293 738 : return;
294 : }
295 :
296 16 : PGconn *hPGConn = poDS->GetPGConn();
297 16 : CPLXMLNode *psMD = oMDMD.Serialize();
298 :
299 16 : if (psMD)
300 : {
301 : // Remove DESCRIPTION and OLMD_FID64 items from metadata
302 :
303 12 : CPLXMLNode *psPrev = nullptr;
304 24 : for (CPLXMLNode *psIter = psMD; psIter;)
305 : {
306 12 : CPLXMLNode *psNext = psIter->psNext;
307 36 : if (psIter->eType == CXT_Element &&
308 24 : strcmp(psIter->pszValue, "Metadata") == 0 &&
309 12 : CPLGetXMLNode(psIter, "domain") == nullptr)
310 : {
311 12 : bool bFoundInterestingItems = false;
312 28 : for (CPLXMLNode *psIter2 = psIter->psChild; psIter2;)
313 : {
314 16 : CPLXMLNode *psNext2 = psIter2->psNext;
315 48 : if (psIter2->eType == CXT_Element &&
316 32 : strcmp(psIter2->pszValue, "MDI") == 0 &&
317 16 : (EQUAL(CPLGetXMLValue(psIter2, "key", ""),
318 14 : OLMD_FID64) ||
319 14 : EQUAL(CPLGetXMLValue(psIter2, "key", ""),
320 : "DESCRIPTION")))
321 : {
322 10 : CPLRemoveXMLChild(psIter, psIter2);
323 : }
324 : else
325 : {
326 6 : bFoundInterestingItems = true;
327 : }
328 16 : psIter2 = psNext2;
329 : }
330 12 : if (!bFoundInterestingItems)
331 : {
332 8 : if (psPrev)
333 0 : psPrev->psNext = psNext;
334 : else
335 8 : psMD = psNext;
336 8 : psIter->psNext = nullptr;
337 8 : CPLDestroyXMLNode(psIter);
338 : }
339 : }
340 12 : psIter = psNext;
341 12 : psPrev = psIter;
342 : }
343 : }
344 :
345 16 : const bool bIsUserTransactionActive = poDS->IsUserTransactionActive();
346 : {
347 16 : PGresult *hResult = OGRPG_PQexec(
348 : hPGConn, bIsUserTransactionActive
349 : ? "SAVEPOINT ogr_system_tables_metadata_savepoint"
350 16 : : "BEGIN");
351 16 : OGRPGClearResult(hResult);
352 : }
353 :
354 16 : if (psMD)
355 : {
356 8 : if (poDS->CreateMetadataTableIfNeeded() &&
357 4 : poDS->HasWritePermissionsOnMetadataTable())
358 : {
359 6 : CPLString osCommand;
360 : osCommand.Printf("DELETE FROM ogr_system_tables.metadata WHERE "
361 : "schema_name = %s AND table_name = %s",
362 6 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
363 9 : OGRPGEscapeString(hPGConn, pszTableName).c_str());
364 3 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
365 3 : OGRPGClearResult(hResult);
366 :
367 : CPLXMLNode *psRoot =
368 3 : CPLCreateXMLNode(nullptr, CXT_Element, "GDALMetadata");
369 3 : CPLAddXMLChild(psRoot, psMD);
370 3 : char *pszXML = CPLSerializeXMLTree(psRoot);
371 : // CPLDebug("PG", "Serializing %s", pszXML);
372 :
373 : osCommand.Printf(
374 : "INSERT INTO ogr_system_tables.metadata (schema_name, "
375 : "table_name, metadata) VALUES (%s, %s, %s)",
376 6 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
377 6 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
378 12 : OGRPGEscapeString(hPGConn, pszXML).c_str());
379 3 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
380 3 : OGRPGClearResult(hResult);
381 :
382 3 : CPLDestroyXMLNode(psRoot);
383 3 : CPLFree(pszXML);
384 : }
385 : }
386 19 : else if (poDS->HasOgrSystemTablesMetadataTable() &&
387 7 : poDS->HasWritePermissionsOnMetadataTable())
388 : {
389 14 : CPLString osCommand;
390 : osCommand.Printf("DELETE FROM ogr_system_tables.metadata WHERE "
391 : "schema_name = %s AND table_name = %s",
392 14 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
393 21 : OGRPGEscapeString(hPGConn, pszTableName).c_str());
394 : PGresult *hResult =
395 7 : OGRPG_PQexec(hPGConn, osCommand.c_str(), false, true);
396 7 : OGRPGClearResult(hResult);
397 : }
398 :
399 : {
400 16 : PGresult *hResult = OGRPG_PQexec(
401 : hPGConn,
402 : bIsUserTransactionActive
403 : ? "RELEASE SAVEPOINT ogr_system_tables_metadata_savepoint"
404 16 : : "COMMIT");
405 16 : OGRPGClearResult(hResult);
406 : }
407 : }
408 :
409 : /************************************************************************/
410 : /* GetMetadataDomainList() */
411 : /************************************************************************/
412 :
413 7 : char **OGRPGTableLayer::GetMetadataDomainList()
414 : {
415 7 : LoadMetadata();
416 :
417 7 : if (m_pszTableDescription == nullptr)
418 4 : GetMetadata();
419 7 : if (m_pszTableDescription != nullptr && m_pszTableDescription[0] != '\0')
420 4 : return CSLAddString(nullptr, "");
421 3 : return nullptr;
422 : }
423 :
424 : /************************************************************************/
425 : /* GetMetadata() */
426 : /************************************************************************/
427 :
428 55 : CSLConstList OGRPGTableLayer::GetMetadata(const char *pszDomain)
429 : {
430 55 : LoadMetadata();
431 :
432 55 : if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
433 53 : m_pszTableDescription == nullptr)
434 : {
435 22 : PGconn *hPGConn = poDS->GetPGConn();
436 44 : CPLString osCommand;
437 : osCommand.Printf("SELECT d.description FROM pg_class c "
438 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
439 : "JOIN pg_description d "
440 : "ON d.objoid = c.oid AND d.classoid = "
441 : "'pg_class'::regclass::oid AND d.objsubid = 0 "
442 : "WHERE c.relname = %s AND n.nspname = %s AND "
443 : "c.relkind in ('r', 'v') ",
444 44 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
445 66 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
446 22 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
447 :
448 22 : const char *pszDesc = nullptr;
449 44 : if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult) &&
450 22 : PQntuples(hResult) == 1)
451 : {
452 8 : pszDesc = PQgetvalue(hResult, 0, 0);
453 8 : if (pszDesc)
454 8 : OGRLayer::SetMetadataItem("DESCRIPTION", pszDesc);
455 : }
456 22 : m_pszTableDescription = CPLStrdup(pszDesc ? pszDesc : "");
457 :
458 22 : OGRPGClearResult(hResult);
459 : }
460 :
461 55 : return OGRLayer::GetMetadata(pszDomain);
462 : }
463 :
464 : /************************************************************************/
465 : /* GetMetadataItem() */
466 : /************************************************************************/
467 :
468 25 : const char *OGRPGTableLayer::GetMetadataItem(const char *pszName,
469 : const char *pszDomain)
470 : {
471 25 : LoadMetadata();
472 :
473 25 : GetMetadata(pszDomain);
474 25 : return OGRLayer::GetMetadataItem(pszName, pszDomain);
475 : }
476 :
477 : /************************************************************************/
478 : /* SetMetadata() */
479 : /************************************************************************/
480 :
481 21 : CPLErr OGRPGTableLayer::SetMetadata(CSLConstList papszMD, const char *pszDomain)
482 : {
483 21 : LoadMetadata();
484 :
485 21 : OGRLayer::SetMetadata(papszMD, pszDomain);
486 21 : m_bMetadataModified = true;
487 :
488 26 : if (!osForcedDescription.empty() &&
489 5 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
490 : {
491 5 : OGRLayer::SetMetadataItem("DESCRIPTION", osForcedDescription);
492 : }
493 :
494 21 : if (!bDeferredCreation && (pszDomain == nullptr || EQUAL(pszDomain, "")))
495 : {
496 20 : const char *pszDescription = OGRLayer::GetMetadataItem("DESCRIPTION");
497 20 : if (pszDescription == nullptr)
498 10 : pszDescription = "";
499 20 : PGconn *hPGConn = poDS->GetPGConn();
500 20 : CPLString osCommand;
501 :
502 : osCommand.Printf(
503 : "COMMENT ON TABLE %s IS %s", pszSqlTableName,
504 20 : pszDescription[0] != '\0'
505 30 : ? OGRPGEscapeString(hPGConn, pszDescription).c_str()
506 30 : : "NULL");
507 20 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
508 20 : OGRPGClearResult(hResult);
509 :
510 20 : CPLFree(m_pszTableDescription);
511 20 : m_pszTableDescription = CPLStrdup(pszDescription);
512 : }
513 :
514 21 : return CE_None;
515 : }
516 :
517 : /************************************************************************/
518 : /* SetMetadataItem() */
519 : /************************************************************************/
520 :
521 15 : CPLErr OGRPGTableLayer::SetMetadataItem(const char *pszName,
522 : const char *pszValue,
523 : const char *pszDomain)
524 : {
525 15 : LoadMetadata();
526 :
527 15 : if ((pszDomain == nullptr || EQUAL(pszDomain, "")) && pszName != nullptr &&
528 30 : EQUAL(pszName, "DESCRIPTION") && !osForcedDescription.empty())
529 : {
530 4 : pszValue = osForcedDescription;
531 : }
532 :
533 15 : OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
534 15 : m_bMetadataModified = true;
535 :
536 15 : if (!bDeferredCreation && (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
537 12 : pszName != nullptr && EQUAL(pszName, "DESCRIPTION"))
538 : {
539 8 : SetMetadata(GetMetadata());
540 : }
541 :
542 15 : return CE_None;
543 : }
544 :
545 : /************************************************************************/
546 : /* SetForcedDescription() */
547 : /************************************************************************/
548 :
549 2 : void OGRPGTableLayer::SetForcedDescription(const char *pszDescriptionIn)
550 : {
551 2 : osForcedDescription = pszDescriptionIn;
552 2 : CPLFree(m_pszTableDescription);
553 2 : m_pszTableDescription = CPLStrdup(pszDescriptionIn);
554 2 : SetMetadataItem("DESCRIPTION", osForcedDescription);
555 2 : }
556 :
557 : /************************************************************************/
558 : /* SetGeometryInformation() */
559 : /************************************************************************/
560 :
561 46 : void OGRPGTableLayer::SetGeometryInformation(PGGeomColumnDesc *pasDesc,
562 : int nGeomFieldCount)
563 : {
564 : // Flag must be set before instantiating geometry fields.
565 46 : bGeometryInformationSet = TRUE;
566 92 : auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer(false));
567 :
568 106 : for (int i = 0; i < nGeomFieldCount; i++)
569 : {
570 : auto poGeomFieldDefn =
571 60 : std::make_unique<OGRPGGeomFieldDefn>(this, pasDesc[i].pszName);
572 60 : poGeomFieldDefn->SetNullable(pasDesc[i].bNullable);
573 60 : poGeomFieldDefn->nSRSId = pasDesc[i].nSRID;
574 60 : poGeomFieldDefn->GeometryTypeFlags = pasDesc[i].GeometryTypeFlags;
575 60 : poGeomFieldDefn->ePostgisType = pasDesc[i].ePostgisType;
576 60 : if (pasDesc[i].pszGeomType != nullptr)
577 : {
578 : OGRwkbGeometryType eGeomType =
579 58 : OGRFromOGCGeomType(pasDesc[i].pszGeomType);
580 58 : if ((poGeomFieldDefn->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
581 : (eGeomType != wkbUnknown))
582 21 : eGeomType = wkbSetZ(eGeomType);
583 58 : if ((poGeomFieldDefn->GeometryTypeFlags &
584 58 : OGRGeometry::OGR_G_MEASURED) &&
585 : (eGeomType != wkbUnknown))
586 1 : eGeomType = wkbSetM(eGeomType);
587 58 : poGeomFieldDefn->SetType(eGeomType);
588 : }
589 60 : poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
590 : }
591 46 : }
592 :
593 : /************************************************************************/
594 : /* ReadTableDefinition() */
595 : /* */
596 : /* Build a schema from the named table. Done by querying the */
597 : /* catalog. */
598 : /************************************************************************/
599 :
600 231316 : int OGRPGTableLayer::ReadTableDefinition()
601 :
602 : {
603 231316 : PGconn *hPGConn = poDS->GetPGConn();
604 :
605 231316 : if (bTableDefinitionValid >= 0)
606 230852 : return bTableDefinitionValid;
607 464 : bTableDefinitionValid = FALSE;
608 :
609 464 : poDS->EndCopy();
610 :
611 928 : auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer());
612 :
613 : /* -------------------------------------------------------------------- */
614 : /* Get the OID of the table. */
615 : /* -------------------------------------------------------------------- */
616 :
617 928 : CPLString osCommand;
618 : osCommand.Printf("SELECT c.oid FROM pg_class c "
619 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
620 : "WHERE c.relname = %s AND n.nspname = %s",
621 928 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
622 1392 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
623 464 : unsigned int nTableOID = 0;
624 : {
625 464 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
626 464 : if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult))
627 : {
628 464 : if (PQntuples(hResult) == 1 && PQgetisnull(hResult, 0, 0) == false)
629 : {
630 222 : nTableOID = static_cast<unsigned>(
631 222 : CPLAtoGIntBig(PQgetvalue(hResult, 0, 0)));
632 222 : OGRPGClearResult(hResult);
633 : }
634 : else
635 : {
636 242 : CPLDebug("PG", "Could not retrieve table oid for %s",
637 : pszTableName);
638 242 : OGRPGClearResult(hResult);
639 242 : return FALSE;
640 : }
641 : }
642 : else
643 : {
644 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
645 : PQerrorMessage(hPGConn));
646 0 : return FALSE;
647 : }
648 : }
649 :
650 : /* -------------------------------------------------------------------- */
651 : /* Identify the integer primary key. */
652 : /* -------------------------------------------------------------------- */
653 :
654 : osCommand.Printf(
655 : "SELECT a.attname, a.attnum, t.typname, "
656 : "t.typname = ANY(ARRAY['int2','int4','int8','serial','bigserial']) AS "
657 : "isfid "
658 : "FROM pg_attribute a "
659 : "JOIN pg_type t ON t.oid = a.atttypid "
660 : "JOIN pg_index i ON i.indrelid = a.attrelid "
661 : "WHERE a.attnum > 0 AND a.attrelid = %u "
662 : "AND i.indisprimary = 't' "
663 : "AND t.typname !~ '^geom' "
664 : "AND a.attnum = ANY(i.indkey) ORDER BY a.attnum",
665 222 : nTableOID);
666 :
667 222 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
668 :
669 222 : if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult))
670 : {
671 222 : if (PQntuples(hResult) == 1 && PQgetisnull(hResult, 0, 0) == false)
672 : {
673 : /* Check if single-field PK can be represented as integer. */
674 346 : CPLString osValue(PQgetvalue(hResult, 0, 3));
675 173 : if (osValue == "t")
676 : {
677 173 : osPrimaryKey.Printf("%s", PQgetvalue(hResult, 0, 0));
678 173 : const char *pszFIDType = PQgetvalue(hResult, 0, 2);
679 173 : CPLDebug("PG", "Primary key name (FID): %s, type : %s",
680 : osPrimaryKey.c_str(), pszFIDType);
681 173 : if (EQUAL(pszFIDType, "int8"))
682 2 : OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
683 : }
684 : }
685 49 : else if (PQntuples(hResult) > 1)
686 : {
687 0 : CPLError(CE_Warning, CPLE_AppDefined,
688 : "Multi-column primary key in \'%s\' detected but not "
689 : "supported.",
690 : pszTableName);
691 : }
692 :
693 222 : OGRPGClearResult(hResult);
694 : /* Zero tuples means no PK is defined, perfectly valid case. */
695 : }
696 : else
697 : {
698 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
699 : }
700 :
701 : /* -------------------------------------------------------------------- */
702 : /* Fire off commands to get back the columns of the table. */
703 : /* -------------------------------------------------------------------- */
704 : osCommand.Printf(
705 : "SELECT a.attname, t.typname, a.attlen,"
706 : " format_type(a.atttypid,a.atttypmod), a.attnotnull, def.def, "
707 : "i.indisunique, descr.description%s "
708 : "FROM pg_attribute a "
709 : "JOIN pg_type t ON t.oid = a.atttypid "
710 : "LEFT JOIN "
711 : "(SELECT adrelid, adnum, pg_get_expr(adbin, adrelid) AS def FROM "
712 : "pg_attrdef) def "
713 : "ON def.adrelid = a.attrelid AND def.adnum = a.attnum "
714 : // Find unique constraints that are on a single column only
715 : "LEFT JOIN "
716 : "(SELECT DISTINCT indrelid, indkey, indisunique FROM pg_index WHERE "
717 : "indisunique) i "
718 : "ON i.indrelid = a.attrelid AND i.indkey[0] = a.attnum AND i.indkey[1] "
719 : "IS NULL "
720 : "LEFT JOIN pg_description descr "
721 : "ON descr.objoid = a.attrelid "
722 : "AND descr.classoid = 'pg_class'::regclass::oid "
723 : "AND descr.objsubid = a.attnum "
724 : "WHERE a.attnum > 0 AND a.attrelid = %u "
725 : "ORDER BY a.attnum",
726 222 : (poDS->sPostgreSQLVersion.nMajor >= 12 ? ", a.attgenerated" : ""),
727 222 : nTableOID);
728 :
729 222 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
730 :
731 222 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
732 : {
733 0 : OGRPGClearResult(hResult);
734 :
735 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
736 0 : return bTableDefinitionValid;
737 : }
738 :
739 222 : if (PQntuples(hResult) == 0)
740 : {
741 0 : OGRPGClearResult(hResult);
742 :
743 0 : CPLDebug("PG", "No field definitions found for '%s', is it a table?",
744 : pszTableName);
745 0 : return bTableDefinitionValid;
746 : }
747 :
748 : /* -------------------------------------------------------------------- */
749 : /* Parse the returned table information. */
750 : /* -------------------------------------------------------------------- */
751 1372 : for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
752 : {
753 1150 : OGRFieldDefn oField(PQgetvalue(hResult, iRecord, 0), OFTString);
754 :
755 1150 : const char *pszType = PQgetvalue(hResult, iRecord, 1);
756 1150 : int nWidth = atoi(PQgetvalue(hResult, iRecord, 2));
757 1150 : const char *pszFormatType = PQgetvalue(hResult, iRecord, 3);
758 1150 : const char *pszNotNull = PQgetvalue(hResult, iRecord, 4);
759 1150 : const char *pszDefault = PQgetisnull(hResult, iRecord, 5)
760 1150 : ? nullptr
761 194 : : PQgetvalue(hResult, iRecord, 5);
762 1150 : const char *pszIsUnique = PQgetvalue(hResult, iRecord, 6);
763 1150 : const char *pszDescription = PQgetvalue(hResult, iRecord, 7);
764 1150 : const char *pszGenerated = poDS->sPostgreSQLVersion.nMajor >= 12
765 1150 : ? PQgetvalue(hResult, iRecord, 8)
766 1150 : : "";
767 :
768 1150 : if (pszNotNull && EQUAL(pszNotNull, "t"))
769 187 : oField.SetNullable(FALSE);
770 1150 : if (pszIsUnique && EQUAL(pszIsUnique, "t"))
771 189 : oField.SetUnique(TRUE);
772 :
773 1150 : if (EQUAL(oField.GetNameRef(), osPrimaryKey))
774 : {
775 193 : pszFIDColumn = CPLStrdup(oField.GetNameRef());
776 193 : CPLDebug("PG", "Using column '%s' as FID for table '%s'",
777 : pszFIDColumn, pszTableName);
778 193 : continue;
779 : }
780 1774 : else if (EQUAL(pszType, "geometry") || EQUAL(pszType, "geography") ||
781 817 : EQUAL(oField.GetNameRef(), "WKB_GEOMETRY"))
782 : {
783 : const auto InitGeomField =
784 532 : [this, &pszType, &oField](OGRPGGeomFieldDefn *poGeomFieldDefn)
785 : {
786 196 : if (EQUAL(pszType, "geometry"))
787 126 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY;
788 70 : else if (EQUAL(pszType, "geography"))
789 : {
790 5 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY;
791 5 : if (!(poDS->sPostGISVersion.nMajor >= 3 ||
792 0 : (poDS->sPostGISVersion.nMajor == 2 &&
793 0 : poDS->sPostGISVersion.nMinor >= 2)))
794 : {
795 : // EPSG:4326 was a requirement for geography before
796 : // PostGIS 2.2
797 0 : poGeomFieldDefn->nSRSId = 4326;
798 : }
799 : }
800 : else
801 : {
802 65 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB;
803 65 : if (EQUAL(pszType, "OID"))
804 0 : bWkbAsOid = TRUE;
805 : }
806 196 : poGeomFieldDefn->SetNullable(oField.IsNullable());
807 401 : };
808 :
809 205 : if (!bGeometryInformationSet)
810 : {
811 193 : if (pszGeomColForced == nullptr ||
812 14 : EQUAL(pszGeomColForced, oField.GetNameRef()))
813 : {
814 : auto poGeomFieldDefn = std::make_unique<OGRPGGeomFieldDefn>(
815 170 : this, oField.GetNameRef());
816 170 : InitGeomField(poGeomFieldDefn.get());
817 170 : poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
818 : }
819 : }
820 : else
821 : {
822 26 : int idx = poFeatureDefn->GetGeomFieldIndex(oField.GetNameRef());
823 26 : if (idx >= 0)
824 : {
825 26 : auto poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(idx);
826 26 : InitGeomField(poGeomFieldDefn);
827 : }
828 : }
829 :
830 205 : continue;
831 : }
832 :
833 752 : OGRPGCommonLayerSetType(oField, pszType, pszFormatType, nWidth);
834 :
835 752 : if (pszDefault)
836 : {
837 27 : OGRPGCommonLayerNormalizeDefault(&oField, pszDefault);
838 : }
839 752 : if (pszDescription)
840 752 : oField.SetComment(pszDescription);
841 :
842 752 : oField.SetGenerated(pszGenerated != nullptr && pszGenerated[0] != '\0');
843 :
844 : // CPLDebug("PG", "name=%s, type=%s", oField.GetNameRef(), pszType);
845 752 : poFeatureDefn->AddFieldDefn(&oField);
846 : }
847 :
848 222 : OGRPGClearResult(hResult);
849 :
850 222 : bTableDefinitionValid = TRUE;
851 :
852 222 : ResetReading();
853 :
854 : /* If geometry type, SRID, etc... have always been set by
855 : * SetGeometryInformation() */
856 : /* no need to issue a new SQL query. Just record the geom type in the layer
857 : * definition */
858 222 : if (bGeometryInformationSet)
859 : {
860 19 : return TRUE;
861 : }
862 203 : bGeometryInformationSet = TRUE;
863 :
864 : // get layer geometry type (for PostGIS dataset)
865 373 : for (int iField = 0; iField < poFeatureDefn->GetGeomFieldCount(); iField++)
866 : {
867 : OGRPGGeomFieldDefn *poGeomFieldDefn =
868 170 : poFeatureDefn->GetGeomFieldDefn(iField);
869 :
870 : /* Get the geometry type and dimensions from the table, or */
871 : /* from its parents if it is a derived table, or from the parent of the
872 : * parent, etc.. */
873 170 : int bGoOn = poDS->m_bHasGeometryColumns;
874 170 : const bool bHasPostGISGeometry =
875 170 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY);
876 :
877 343 : while (bGoOn)
878 : {
879 : const CPLString osEscapedThisOrParentTableName(OGRPGEscapeString(
880 173 : hPGConn, (pszSqlGeomParentTableName) ? pszSqlGeomParentTableName
881 346 : : pszTableName));
882 : osCommand.Printf("SELECT type, coord_dimension, srid FROM %s WHERE "
883 : "f_table_name = %s",
884 : (bHasPostGISGeometry) ? "geometry_columns"
885 : : "geography_columns",
886 173 : osEscapedThisOrParentTableName.c_str());
887 :
888 173 : osCommand += CPLString().Printf(
889 : " AND %s=%s",
890 : (bHasPostGISGeometry) ? "f_geometry_column"
891 : : "f_geography_column",
892 346 : OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef())
893 173 : .c_str());
894 :
895 173 : osCommand += CPLString().Printf(
896 : " AND f_table_schema = %s",
897 173 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
898 :
899 173 : hResult = OGRPG_PQexec(hPGConn, osCommand);
900 :
901 278 : if (hResult && PQntuples(hResult) == 1 &&
902 105 : !PQgetisnull(hResult, 0, 0))
903 : {
904 105 : const char *pszType = PQgetvalue(hResult, 0, 0);
905 :
906 105 : int dim = atoi(PQgetvalue(hResult, 0, 1));
907 105 : bool bHasM = pszType[strlen(pszType) - 1] == 'M';
908 105 : int GeometryTypeFlags = 0;
909 105 : if (dim == 3)
910 : {
911 29 : if (bHasM)
912 0 : GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
913 : else
914 29 : GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
915 : }
916 76 : else if (dim == 4)
917 3 : GeometryTypeFlags |=
918 : OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
919 :
920 105 : int nSRSId = atoi(PQgetvalue(hResult, 0, 2));
921 :
922 105 : poGeomFieldDefn->GeometryTypeFlags = GeometryTypeFlags;
923 105 : if (nSRSId > 0)
924 18 : poGeomFieldDefn->nSRSId = nSRSId;
925 105 : OGRwkbGeometryType eGeomType = OGRFromOGCGeomType(pszType);
926 105 : if (poGeomFieldDefn->GeometryTypeFlags &
927 32 : OGRGeometry::OGR_G_3D &&
928 : eGeomType != wkbUnknown)
929 8 : eGeomType = wkbSetZ(eGeomType);
930 105 : if (poGeomFieldDefn->GeometryTypeFlags &
931 3 : OGRGeometry::OGR_G_MEASURED &&
932 : eGeomType != wkbUnknown)
933 0 : eGeomType = wkbSetM(eGeomType);
934 105 : poGeomFieldDefn->SetType(eGeomType);
935 :
936 105 : bGoOn = FALSE;
937 : }
938 : else
939 : {
940 : /* Fetch the name of the parent table */
941 : osCommand.Printf(
942 : "SELECT pg_class.relname FROM pg_class WHERE oid = "
943 : "(SELECT pg_inherits.inhparent FROM pg_inherits WHERE "
944 : "inhrelid = "
945 : "(SELECT c.oid FROM pg_class c, pg_namespace n "
946 : "WHERE c.relname = %s AND c.relnamespace=n.oid AND "
947 : "n.nspname = %s))",
948 : osEscapedThisOrParentTableName.c_str(),
949 68 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
950 :
951 68 : OGRPGClearResult(hResult);
952 68 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
953 :
954 71 : if (hResult && PQntuples(hResult) == 1 &&
955 3 : !PQgetisnull(hResult, 0, 0))
956 : {
957 3 : CPLFree(pszSqlGeomParentTableName);
958 3 : pszSqlGeomParentTableName =
959 3 : CPLStrdup(PQgetvalue(hResult, 0, 0));
960 : }
961 : else
962 : {
963 : /* No more parent : stop recursion */
964 65 : bGoOn = FALSE;
965 : }
966 : }
967 :
968 173 : OGRPGClearResult(hResult);
969 : }
970 : }
971 :
972 203 : return bTableDefinitionValid;
973 : }
974 :
975 : /************************************************************************/
976 : /* SetTableDefinition() */
977 : /************************************************************************/
978 :
979 234 : void OGRPGTableLayer::SetTableDefinition(const char *pszFIDColumnName,
980 : const char *pszGFldName,
981 : OGRwkbGeometryType eType,
982 : const char *pszGeomType, int nSRSId,
983 : int GeometryTypeFlags)
984 : {
985 234 : bTableDefinitionValid = TRUE;
986 234 : bGeometryInformationSet = TRUE;
987 234 : pszFIDColumn = CPLStrdup(pszFIDColumnName);
988 468 : auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer());
989 234 : poFeatureDefn->SetGeomType(wkbNone);
990 234 : if (eType != wkbNone)
991 : {
992 : auto poGeomFieldDefn =
993 205 : std::make_unique<OGRPGGeomFieldDefn>(this, pszGFldName);
994 205 : poGeomFieldDefn->SetType(eType);
995 205 : poGeomFieldDefn->GeometryTypeFlags = GeometryTypeFlags;
996 :
997 205 : if (EQUAL(pszGeomType, "geometry"))
998 : {
999 112 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY;
1000 112 : poGeomFieldDefn->nSRSId = nSRSId;
1001 : }
1002 93 : else if (EQUAL(pszGeomType, "geography"))
1003 : {
1004 6 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY;
1005 6 : poGeomFieldDefn->nSRSId = nSRSId;
1006 : }
1007 : else
1008 : {
1009 87 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB;
1010 87 : if (EQUAL(pszGeomType, "OID"))
1011 0 : bWkbAsOid = TRUE;
1012 : }
1013 205 : poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
1014 : }
1015 29 : else if (pszGFldName != nullptr)
1016 : {
1017 2 : m_osFirstGeometryFieldName = pszGFldName;
1018 : }
1019 234 : m_osLCOGeomType = pszGeomType;
1020 234 : }
1021 :
1022 : /************************************************************************/
1023 : /* ISetSpatialFilter() */
1024 : /************************************************************************/
1025 :
1026 98 : OGRErr OGRPGTableLayer::ISetSpatialFilter(int iGeomField,
1027 : const OGRGeometry *poGeomIn)
1028 :
1029 : {
1030 98 : m_iGeomFieldFilter = iGeomField;
1031 :
1032 98 : if (InstallFilter(poGeomIn))
1033 : {
1034 57 : BuildWhere();
1035 :
1036 57 : ResetReading();
1037 : }
1038 :
1039 98 : return OGRERR_NONE;
1040 : }
1041 :
1042 : /************************************************************************/
1043 : /* BuildWhere() */
1044 : /* */
1045 : /* Build the WHERE statement appropriate to the current set of */
1046 : /* criteria (spatial and attribute queries). */
1047 : /************************************************************************/
1048 :
1049 223 : void OGRPGTableLayer::BuildWhere()
1050 :
1051 : {
1052 223 : osWHERE = "";
1053 223 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
1054 223 : if (poFeatureDefn->GetGeomFieldCount() != 0)
1055 219 : poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
1056 :
1057 223 : if (m_poFilterGeom != nullptr && poGeomFieldDefn != nullptr &&
1058 61 : poDS->sPostGISVersion.nMajor >= 0 &&
1059 53 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
1060 20 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY))
1061 : {
1062 35 : poGeomFieldDefn->GetSpatialRef(); // make sure nSRSId is resolved
1063 35 : if (!poDS->IsSpatialFilterIntersectionLocal())
1064 : {
1065 4 : char *pszHexEWKB = OGRGeometryToHexEWKB(
1066 2 : m_poFilterGeom, poGeomFieldDefn->nSRSId,
1067 2 : poDS->sPostGISVersion.nMajor, poDS->sPostGISVersion.nMinor);
1068 : // Note that we purposely do ::GEOMETRY intersection even
1069 : // on geography case
1070 : osWHERE.Printf(
1071 : "WHERE ST_Intersects(%s::GEOMETRY, '%s'::GEOMETRY) ",
1072 4 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
1073 4 : pszHexEWKB);
1074 2 : CPLFree(pszHexEWKB);
1075 : }
1076 : else
1077 : {
1078 33 : OGREnvelope sEnvelope;
1079 :
1080 33 : m_poFilterGeom->getEnvelope(&sEnvelope);
1081 33 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1082 : {
1083 1 : if (sEnvelope.MinX < -180.0)
1084 0 : sEnvelope.MinX = -180.0;
1085 1 : if (sEnvelope.MinY < -90.0)
1086 0 : sEnvelope.MinY = -90.0;
1087 1 : if (sEnvelope.MaxX > 180.0)
1088 0 : sEnvelope.MaxX = 180.0;
1089 1 : if (sEnvelope.MaxY > 90.0)
1090 0 : sEnvelope.MaxY = 90.0;
1091 : }
1092 : // Avoid +/- infinity
1093 33 : sEnvelope.MinX =
1094 33 : std::max(sEnvelope.MinX, -std::numeric_limits<double>::max());
1095 33 : sEnvelope.MinY =
1096 33 : std::max(sEnvelope.MinY, -std::numeric_limits<double>::max());
1097 33 : sEnvelope.MaxX =
1098 33 : std::min(sEnvelope.MaxX, std::numeric_limits<double>::max());
1099 33 : sEnvelope.MaxY =
1100 33 : std::min(sEnvelope.MaxY, std::numeric_limits<double>::max());
1101 : osWHERE.Printf(
1102 : "WHERE %s::GEOMETRY && "
1103 : "ST_MakeEnvelope(%.17g,%.17g,%.17g,%.17g) ",
1104 66 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
1105 66 : sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY);
1106 : }
1107 : }
1108 :
1109 223 : if (!osQuery.empty())
1110 : {
1111 89 : if (osWHERE.empty())
1112 : {
1113 82 : osWHERE.Printf("WHERE %s ", osQuery.c_str());
1114 : }
1115 : else
1116 : {
1117 7 : osWHERE += "AND (";
1118 7 : osWHERE += osQuery;
1119 7 : osWHERE += ")";
1120 : }
1121 : }
1122 223 : }
1123 :
1124 : /************************************************************************/
1125 : /* BuildFullQueryStatement() */
1126 : /************************************************************************/
1127 :
1128 1028 : void OGRPGTableLayer::BuildFullQueryStatement()
1129 :
1130 : {
1131 1028 : CPLString osFields = BuildFields();
1132 1028 : if (pszQueryStatement != nullptr)
1133 : {
1134 608 : CPLFree(pszQueryStatement);
1135 608 : pszQueryStatement = nullptr;
1136 : }
1137 2056 : pszQueryStatement = static_cast<char *>(CPLMalloc(
1138 1028 : osFields.size() + osWHERE.size() + strlen(pszSqlTableName) + 40));
1139 1028 : snprintf(pszQueryStatement,
1140 1028 : osFields.size() + osWHERE.size() + strlen(pszSqlTableName) + 40,
1141 : "SELECT %s FROM %s %s", osFields.c_str(), pszSqlTableName,
1142 : osWHERE.c_str());
1143 1028 : }
1144 :
1145 : /************************************************************************/
1146 : /* ResetReading() */
1147 : /************************************************************************/
1148 :
1149 1059 : void OGRPGTableLayer::ResetReading()
1150 :
1151 : {
1152 1059 : if (bInResetReading)
1153 31 : return;
1154 1028 : bInResetReading = TRUE;
1155 :
1156 1028 : if (bDeferredCreation)
1157 16 : RunDeferredCreationIfNecessary();
1158 1028 : poDS->EndCopy();
1159 1028 : bUseCopyByDefault = FALSE;
1160 :
1161 1028 : BuildFullQueryStatement();
1162 :
1163 1028 : OGRPGLayer::ResetReading();
1164 :
1165 1028 : bInResetReading = FALSE;
1166 : }
1167 :
1168 : /************************************************************************/
1169 : /* GetNextFeature() */
1170 : /************************************************************************/
1171 :
1172 1241 : OGRFeature *OGRPGTableLayer::GetNextFeature()
1173 :
1174 : {
1175 1241 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1176 0 : return nullptr;
1177 1241 : poDS->EndCopy();
1178 :
1179 1241 : if (pszQueryStatement == nullptr)
1180 6 : ResetReading();
1181 :
1182 1241 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
1183 1241 : if (poFeatureDefn->GetGeomFieldCount() != 0)
1184 1169 : poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
1185 1241 : poFeatureDefn->GetFieldCount();
1186 :
1187 : while (true)
1188 : {
1189 1345 : OGRFeature *poFeature = GetNextRawFeature();
1190 1345 : if (poFeature == nullptr)
1191 140 : return nullptr;
1192 :
1193 : /* We just have to look if there is a geometry filter */
1194 : /* The attribute filter is always taken into account by the select
1195 : * request */
1196 355 : if (m_poFilterGeom == nullptr || poGeomFieldDefn == nullptr ||
1197 355 : (poDS->sPostGISVersion.nMajor >= 0 &&
1198 1881 : !poDS->IsSpatialFilterIntersectionLocal()) ||
1199 353 : FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter)))
1200 : {
1201 1101 : if (iFIDAsRegularColumnIndex >= 0)
1202 : {
1203 1 : poFeature->SetField(iFIDAsRegularColumnIndex,
1204 : poFeature->GetFID());
1205 : }
1206 1101 : return poFeature;
1207 : }
1208 :
1209 104 : delete poFeature;
1210 104 : }
1211 : }
1212 :
1213 : /************************************************************************/
1214 : /* BuildFields() */
1215 : /* */
1216 : /* Build list of fields to fetch, performing any required */
1217 : /* transformations (such as on geometry). */
1218 : /************************************************************************/
1219 :
1220 1171 : CPLString OGRPGTableLayer::BuildFields()
1221 :
1222 : {
1223 1171 : int i = 0;
1224 1171 : CPLString osFieldList;
1225 :
1226 1171 : poFeatureDefn->GetFieldCount();
1227 :
1228 2299 : if (pszFIDColumn != nullptr &&
1229 1128 : poFeatureDefn->GetFieldIndex(pszFIDColumn) == -1)
1230 : {
1231 1124 : osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
1232 : }
1233 :
1234 2252 : for (i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
1235 : {
1236 : OGRPGGeomFieldDefn *poGeomFieldDefn =
1237 1081 : poFeatureDefn->GetGeomFieldDefn(i);
1238 : CPLString osEscapedGeom =
1239 2162 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
1240 :
1241 1081 : if (!osFieldList.empty())
1242 1054 : osFieldList += ", ";
1243 :
1244 1081 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
1245 : {
1246 634 : if (!poDS->HavePostGIS() || poDS->bUseBinaryCursor)
1247 : {
1248 8 : osFieldList += osEscapedGeom;
1249 : }
1250 626 : else if (CPLTestBool(CPLGetConfigOption("PG_USE_BASE64", "NO")))
1251 : {
1252 1 : osFieldList += "encode(ST_AsEWKB(";
1253 1 : osFieldList += osEscapedGeom;
1254 1 : osFieldList += "), 'base64') AS ";
1255 2 : osFieldList += OGRPGEscapeColumnName(
1256 1 : CPLSPrintf("EWKBBase64_%s", poGeomFieldDefn->GetNameRef()));
1257 : }
1258 : else
1259 : {
1260 : /* This will return EWKB in an hex encoded form */
1261 625 : osFieldList += osEscapedGeom;
1262 : }
1263 : }
1264 447 : else if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1265 : {
1266 : #if defined(BINARY_CURSOR_ENABLED)
1267 : if (poDS->bUseBinaryCursor)
1268 : {
1269 : osFieldList += "ST_AsBinary(";
1270 : osFieldList += osEscapedGeom;
1271 : osFieldList += ") AS";
1272 : osFieldList += OGRPGEscapeColumnName(
1273 : CPLSPrintf("AsBinary_%s", poGeomFieldDefn->GetNameRef()));
1274 : }
1275 : else
1276 : #endif
1277 21 : if (CPLTestBool(CPLGetConfigOption("PG_USE_BASE64", "NO")))
1278 : {
1279 0 : osFieldList += "encode(ST_AsEWKB(";
1280 0 : osFieldList += osEscapedGeom;
1281 0 : osFieldList += "::geometry), 'base64') AS ";
1282 0 : osFieldList += OGRPGEscapeColumnName(
1283 0 : CPLSPrintf("EWKBBase64_%s", poGeomFieldDefn->GetNameRef()));
1284 : }
1285 : else
1286 : {
1287 21 : osFieldList += osEscapedGeom;
1288 : }
1289 : }
1290 : else
1291 : {
1292 426 : osFieldList += osEscapedGeom;
1293 : }
1294 : }
1295 :
1296 5393 : for (i = 0; i < poFeatureDefn->GetFieldCount(); i++)
1297 : {
1298 4222 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
1299 :
1300 4222 : if (!osFieldList.empty())
1301 4202 : osFieldList += ", ";
1302 :
1303 : #if defined(BINARY_CURSOR_ENABLED)
1304 : /* With a binary cursor, it is not possible to get the time zone */
1305 : /* of a timestamptz column. So we fallback to asking it in text mode */
1306 : if (poDS->bUseBinaryCursor &&
1307 : poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
1308 : {
1309 : osFieldList += "CAST (";
1310 : osFieldList += OGRPGEscapeColumnName(pszName);
1311 : osFieldList += " AS text)";
1312 : }
1313 : else
1314 : #endif
1315 : {
1316 4222 : osFieldList += OGRPGEscapeColumnName(pszName);
1317 : }
1318 : }
1319 :
1320 1171 : return osFieldList;
1321 : }
1322 :
1323 : /************************************************************************/
1324 : /* SetAttributeFilter() */
1325 : /************************************************************************/
1326 :
1327 166 : OGRErr OGRPGTableLayer::SetAttributeFilter(const char *pszQuery)
1328 :
1329 : {
1330 166 : CPLFree(m_pszAttrQueryString);
1331 166 : m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : nullptr;
1332 :
1333 166 : if (pszQuery == nullptr)
1334 77 : osQuery = "";
1335 : else
1336 89 : osQuery = pszQuery;
1337 :
1338 166 : BuildWhere();
1339 :
1340 166 : ResetReading();
1341 :
1342 166 : return OGRERR_NONE;
1343 : }
1344 :
1345 : /************************************************************************/
1346 : /* DeleteFeature() */
1347 : /************************************************************************/
1348 :
1349 26 : OGRErr OGRPGTableLayer::DeleteFeature(GIntBig nFID)
1350 :
1351 : {
1352 26 : PGconn *hPGConn = poDS->GetPGConn();
1353 52 : CPLString osCommand;
1354 :
1355 26 : GetLayerDefn()->GetFieldCount();
1356 :
1357 26 : if (!bUpdateAccess)
1358 : {
1359 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1360 : "DeleteFeature");
1361 0 : return OGRERR_FAILURE;
1362 : }
1363 :
1364 26 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1365 0 : return OGRERR_FAILURE;
1366 26 : poDS->EndCopy();
1367 26 : bAutoFIDOnCreateViaCopy = FALSE;
1368 :
1369 : /* -------------------------------------------------------------------- */
1370 : /* We can only delete features if we have a well defined FID */
1371 : /* column to target. */
1372 : /* -------------------------------------------------------------------- */
1373 26 : if (pszFIDColumn == nullptr)
1374 : {
1375 0 : CPLError(CE_Failure, CPLE_AppDefined,
1376 : "DeleteFeature(" CPL_FRMT_GIB
1377 : ") failed. Unable to delete features in tables without\n"
1378 : "a recognised FID column.",
1379 : nFID);
1380 0 : return OGRERR_FAILURE;
1381 : }
1382 :
1383 : /* -------------------------------------------------------------------- */
1384 : /* Form the statement to drop the record. */
1385 : /* -------------------------------------------------------------------- */
1386 : osCommand.Printf("DELETE FROM %s WHERE %s = " CPL_FRMT_GIB, pszSqlTableName,
1387 26 : OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFID);
1388 :
1389 : /* -------------------------------------------------------------------- */
1390 : /* Execute the delete. */
1391 : /* -------------------------------------------------------------------- */
1392 : OGRErr eErr;
1393 :
1394 26 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
1395 :
1396 26 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1397 : {
1398 0 : CPLError(CE_Failure, CPLE_AppDefined,
1399 : "DeleteFeature() DELETE statement failed.\n%s",
1400 : PQerrorMessage(hPGConn));
1401 :
1402 0 : eErr = OGRERR_FAILURE;
1403 : }
1404 : else
1405 : {
1406 26 : if (EQUAL(PQcmdStatus(hResult), "DELETE 0"))
1407 12 : eErr = OGRERR_NON_EXISTING_FEATURE;
1408 : else
1409 14 : eErr = OGRERR_NONE;
1410 : }
1411 :
1412 26 : OGRPGClearResult(hResult);
1413 :
1414 26 : return eErr;
1415 : }
1416 :
1417 : /************************************************************************/
1418 : /* ISetFeature() */
1419 : /* */
1420 : /* SetFeature() is implemented by an UPDATE SQL command */
1421 : /************************************************************************/
1422 :
1423 2053 : OGRErr OGRPGTableLayer::ISetFeature(OGRFeature *poFeature)
1424 :
1425 : {
1426 2053 : return IUpdateFeature(poFeature, -1, nullptr, -1, nullptr, false);
1427 : }
1428 :
1429 : /************************************************************************/
1430 : /* UpdateFeature() */
1431 : /************************************************************************/
1432 :
1433 2071 : OGRErr OGRPGTableLayer::IUpdateFeature(OGRFeature *poFeature,
1434 : int nUpdatedFieldsCount,
1435 : const int *panUpdatedFieldsIdx,
1436 : int nUpdatedGeomFieldsCount,
1437 : const int *panUpdatedGeomFieldsIdx,
1438 : bool /* bUpdateStyleString*/)
1439 :
1440 : {
1441 2071 : PGconn *hPGConn = poDS->GetPGConn();
1442 4142 : CPLString osCommand;
1443 2071 : bool bNeedComma = false;
1444 2071 : OGRErr eErr = OGRERR_FAILURE;
1445 :
1446 2071 : GetLayerDefn()->GetFieldCount();
1447 :
1448 2071 : const char *pszMethodName =
1449 2071 : nUpdatedFieldsCount >= 0 ? "UpdateFeature" : "SetFeature";
1450 2071 : if (!bUpdateAccess)
1451 : {
1452 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1453 : pszMethodName);
1454 0 : return OGRERR_FAILURE;
1455 : }
1456 :
1457 2071 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1458 0 : return OGRERR_FAILURE;
1459 2071 : poDS->EndCopy();
1460 :
1461 2071 : if (nullptr == poFeature)
1462 : {
1463 0 : CPLError(CE_Failure, CPLE_AppDefined,
1464 : "NULL pointer to OGRFeature passed to %s().", pszMethodName);
1465 0 : return eErr;
1466 : }
1467 :
1468 2071 : if (poFeature->GetFID() == OGRNullFID)
1469 : {
1470 2 : CPLError(CE_Failure, CPLE_AppDefined,
1471 : "FID required on features given to %s().", pszMethodName);
1472 2 : return eErr;
1473 : }
1474 :
1475 2069 : if (pszFIDColumn == nullptr)
1476 : {
1477 0 : CPLError(CE_Failure, CPLE_AppDefined,
1478 : "Unable to update features in tables without\n"
1479 : "a recognised FID column.");
1480 0 : return eErr;
1481 : }
1482 :
1483 : /* In case the FID column has also been created as a regular field */
1484 2069 : if (iFIDAsRegularColumnIndex >= 0)
1485 : {
1486 5 : if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex) ||
1487 2 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
1488 2 : poFeature->GetFID())
1489 : {
1490 2 : CPLError(CE_Failure, CPLE_AppDefined,
1491 : "Inconsistent values of FID and field of same name");
1492 2 : return OGRERR_FAILURE;
1493 : }
1494 : }
1495 :
1496 : /* -------------------------------------------------------------------- */
1497 : /* Form the UPDATE command. */
1498 : /* -------------------------------------------------------------------- */
1499 2067 : osCommand.Printf("UPDATE %s SET ", pszSqlTableName);
1500 :
1501 : /* Set the geometry */
1502 : const int nGeomStop = nUpdatedGeomFieldsCount >= 0
1503 2067 : ? nUpdatedGeomFieldsCount
1504 2051 : : poFeatureDefn->GetGeomFieldCount();
1505 4119 : for (int i = 0; i < nGeomStop; i++)
1506 : {
1507 2052 : const int iField =
1508 2052 : nUpdatedGeomFieldsCount >= 0 ? panUpdatedGeomFieldsIdx[i] : i;
1509 : OGRPGGeomFieldDefn *poGeomFieldDefn =
1510 2052 : poFeatureDefn->GetGeomFieldDefn(iField);
1511 2052 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iField);
1512 2052 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB)
1513 : {
1514 1017 : if (bNeedComma)
1515 0 : osCommand += ", ";
1516 : else
1517 1017 : bNeedComma = true;
1518 :
1519 1017 : osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
1520 1017 : osCommand += " = ";
1521 1017 : if (poGeom != nullptr)
1522 : {
1523 11 : if (!bWkbAsOid)
1524 : {
1525 : char *pszBytea =
1526 22 : GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
1527 11 : poDS->sPostGISVersion.nMinor);
1528 :
1529 11 : if (pszBytea != nullptr)
1530 : {
1531 11 : osCommand += "E'";
1532 11 : osCommand += pszBytea;
1533 11 : osCommand += '\'';
1534 11 : CPLFree(pszBytea);
1535 : }
1536 : else
1537 0 : osCommand += "NULL";
1538 : }
1539 : else
1540 : {
1541 0 : Oid oid = GeometryToOID(poGeom);
1542 :
1543 0 : if (oid != 0)
1544 : {
1545 0 : osCommand += CPLString().Printf("'%d' ", oid);
1546 : }
1547 : else
1548 0 : osCommand += "NULL";
1549 : }
1550 : }
1551 : else
1552 1006 : osCommand += "NULL";
1553 : }
1554 1035 : else if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
1555 1034 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
1556 : {
1557 1035 : if (bNeedComma)
1558 0 : osCommand += ", ";
1559 : else
1560 1035 : bNeedComma = true;
1561 :
1562 1035 : osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
1563 1035 : osCommand += " = ";
1564 1035 : if (poGeom != nullptr)
1565 : {
1566 29 : poGeom->closeRings();
1567 29 : poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
1568 29 : OGRGeometry::OGR_G_3D);
1569 29 : poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
1570 29 : OGRGeometry::OGR_G_MEASURED);
1571 : }
1572 :
1573 1035 : if (poGeom != nullptr)
1574 : {
1575 58 : char *pszHexEWKB = OGRGeometryToHexEWKB(
1576 : poGeom, poGeomFieldDefn->nSRSId,
1577 29 : poDS->sPostGISVersion.nMajor, poDS->sPostGISVersion.nMinor);
1578 29 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1579 : osCommand +=
1580 1 : CPLString().Printf("'%s'::GEOGRAPHY", pszHexEWKB);
1581 : else
1582 : osCommand +=
1583 28 : CPLString().Printf("'%s'::GEOMETRY", pszHexEWKB);
1584 29 : CPLFree(pszHexEWKB);
1585 : }
1586 : else
1587 1006 : osCommand += "NULL";
1588 : }
1589 : }
1590 :
1591 2067 : const int nStop = nUpdatedFieldsCount >= 0 ? nUpdatedFieldsCount
1592 2051 : : poFeatureDefn->GetFieldCount();
1593 4260 : for (int i = 0; i < nStop; i++)
1594 : {
1595 2193 : const int iField =
1596 2193 : nUpdatedFieldsCount >= 0 ? panUpdatedFieldsIdx[i] : i;
1597 2193 : if (iFIDAsRegularColumnIndex == iField)
1598 1 : continue;
1599 2192 : if (!poFeature->IsFieldSet(iField))
1600 0 : continue;
1601 2192 : if (poFeature->GetDefnRef()->GetFieldDefn(iField)->IsGenerated())
1602 2 : continue;
1603 :
1604 2190 : if (bNeedComma)
1605 2175 : osCommand += ", ";
1606 : else
1607 15 : bNeedComma = true;
1608 :
1609 4380 : osCommand += OGRPGEscapeColumnName(
1610 4380 : poFeatureDefn->GetFieldDefn(iField)->GetNameRef());
1611 2190 : osCommand += " = ";
1612 :
1613 2190 : if (poFeature->IsFieldNull(iField))
1614 : {
1615 64 : osCommand += "NULL";
1616 : }
1617 : else
1618 : {
1619 2126 : OGRPGCommonAppendFieldValue(osCommand, poFeature, iField,
1620 : OGRPGEscapeString, hPGConn);
1621 : }
1622 : }
1623 2067 : if (!bNeedComma) // nothing to do
1624 0 : return OGRERR_NONE;
1625 :
1626 : /* Add the WHERE clause */
1627 2067 : osCommand += " WHERE ";
1628 2067 : osCommand += OGRPGEscapeColumnName(pszFIDColumn);
1629 2067 : osCommand += +" = ";
1630 2067 : osCommand += CPLString().Printf(CPL_FRMT_GIB, poFeature->GetFID());
1631 :
1632 : /* -------------------------------------------------------------------- */
1633 : /* Execute the update. */
1634 : /* -------------------------------------------------------------------- */
1635 2067 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
1636 2067 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1637 : {
1638 0 : CPLError(CE_Failure, CPLE_AppDefined,
1639 : "UPDATE command for feature " CPL_FRMT_GIB
1640 : " failed.\n%s\nCommand: %s",
1641 : poFeature->GetFID(), PQerrorMessage(hPGConn),
1642 : osCommand.c_str());
1643 :
1644 0 : OGRPGClearResult(hResult);
1645 :
1646 0 : return OGRERR_FAILURE;
1647 : }
1648 :
1649 2067 : if (EQUAL(PQcmdStatus(hResult), "UPDATE 0"))
1650 9 : eErr = OGRERR_NON_EXISTING_FEATURE;
1651 : else
1652 2058 : eErr = OGRERR_NONE;
1653 :
1654 2067 : OGRPGClearResult(hResult);
1655 :
1656 2067 : return eErr;
1657 : }
1658 :
1659 : /************************************************************************/
1660 : /* ICreateFeature() */
1661 : /************************************************************************/
1662 :
1663 3883 : OGRErr OGRPGTableLayer::ICreateFeature(OGRFeature *poFeature)
1664 : {
1665 3883 : GetLayerDefn()->GetFieldCount();
1666 :
1667 3883 : if (!bUpdateAccess)
1668 : {
1669 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1670 : "CreateFeature");
1671 0 : return OGRERR_FAILURE;
1672 : }
1673 :
1674 3883 : if (nullptr == poFeature)
1675 : {
1676 0 : CPLError(CE_Failure, CPLE_AppDefined,
1677 : "NULL pointer to OGRFeature passed to CreateFeature().");
1678 0 : return OGRERR_FAILURE;
1679 : }
1680 :
1681 3883 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1682 0 : return OGRERR_FAILURE;
1683 :
1684 : /* In case the FID column has also been created as a regular field */
1685 3883 : GIntBig nFID = poFeature->GetFID();
1686 3883 : if (iFIDAsRegularColumnIndex >= 0)
1687 : {
1688 4 : if (nFID == OGRNullFID)
1689 : {
1690 3 : if (poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex))
1691 : {
1692 2 : poFeature->SetFID(
1693 2 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex));
1694 : }
1695 : }
1696 : else
1697 : {
1698 2 : if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex) ||
1699 1 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
1700 : nFID)
1701 : {
1702 1 : CPLError(CE_Failure, CPLE_AppDefined,
1703 : "Inconsistent values of FID and field of same name");
1704 1 : return OGRERR_FAILURE;
1705 : }
1706 : }
1707 : }
1708 :
1709 : /* Auto-promote FID column to 64bit if necessary */
1710 3886 : if (pszFIDColumn != nullptr && !CPL_INT64_FITS_ON_INT32(nFID) &&
1711 4 : OGRLayer::GetMetadataItem(OLMD_FID64) == nullptr)
1712 : {
1713 2 : poDS->EndCopy();
1714 :
1715 2 : CPLString osCommand;
1716 : osCommand.Printf("ALTER TABLE %s ALTER COLUMN %s TYPE INT8",
1717 : pszSqlTableName,
1718 2 : OGRPGEscapeColumnName(pszFIDColumn).c_str());
1719 2 : PGconn *hPGConn = poDS->GetPGConn();
1720 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
1721 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1722 : {
1723 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
1724 : PQerrorMessage(hPGConn));
1725 :
1726 0 : OGRPGClearResult(hResult);
1727 :
1728 0 : return OGRERR_FAILURE;
1729 : }
1730 2 : OGRPGClearResult(hResult);
1731 :
1732 2 : OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
1733 : }
1734 :
1735 3882 : if (bFirstInsertion)
1736 : {
1737 204 : bFirstInsertion = FALSE;
1738 204 : if (CPLTestBool(CPLGetConfigOption("OGR_TRUNCATE", "NO")))
1739 : {
1740 1 : PGconn *hPGConn = poDS->GetPGConn();
1741 2 : CPLString osCommand;
1742 :
1743 1 : osCommand.Printf("TRUNCATE TABLE %s", pszSqlTableName);
1744 1 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1745 1 : OGRPGClearResult(hResult);
1746 : }
1747 : }
1748 :
1749 : // We avoid testing the config option too often.
1750 3882 : if (bUseCopy == USE_COPY_UNSET)
1751 54 : bUseCopy = CPLTestBool(CPLGetConfigOption("PG_USE_COPY", "NO"));
1752 :
1753 : OGRErr eErr;
1754 3882 : if (!bUseCopy)
1755 : {
1756 121 : eErr = CreateFeatureViaInsert(poFeature);
1757 : }
1758 : else
1759 : {
1760 : /* If there's a unset field with a default value, then we must use */
1761 : /* a specific INSERT statement to avoid unset fields to be bound to NULL
1762 : */
1763 3761 : bool bHasDefaultValue = false;
1764 3761 : const int nFieldCount = poFeatureDefn->GetFieldCount();
1765 8988 : for (int iField = 0; iField < nFieldCount; iField++)
1766 : {
1767 6487 : if (!poFeature->IsFieldSet(iField) &&
1768 1259 : poFeature->GetFieldDefnRef(iField)->GetDefault() != nullptr)
1769 : {
1770 1 : bHasDefaultValue = true;
1771 1 : break;
1772 : }
1773 : }
1774 3761 : if (bHasDefaultValue)
1775 : {
1776 1 : eErr = CreateFeatureViaInsert(poFeature);
1777 : }
1778 : else
1779 : {
1780 : bool bFIDSet =
1781 3760 : (pszFIDColumn != nullptr && poFeature->GetFID() != OGRNullFID);
1782 3760 : if (bCopyActive && bFIDSet != bFIDColumnInCopyFields)
1783 : {
1784 1 : eErr = CreateFeatureViaInsert(poFeature);
1785 : }
1786 197 : else if (!bCopyActive && poFeatureDefn->GetFieldCount() == 0 &&
1787 3956 : poFeatureDefn->GetGeomFieldCount() == 0 && !bFIDSet)
1788 : {
1789 4 : eErr = CreateFeatureViaInsert(poFeature);
1790 : }
1791 : else
1792 : {
1793 3755 : if (!bCopyActive)
1794 : {
1795 : /* This is a heuristics. If the first feature to be copied
1796 : * has a */
1797 : /* FID set (and that a FID column has been identified), then
1798 : * we will */
1799 : /* try to copy FID values from features. Otherwise, we will
1800 : * not */
1801 : /* do and assume that the FID column is an autoincremented
1802 : * column. */
1803 193 : bFIDColumnInCopyFields = bFIDSet;
1804 193 : bNeedToUpdateSequence = bFIDSet;
1805 : }
1806 :
1807 3755 : eErr = CreateFeatureViaCopy(poFeature);
1808 3755 : if (bFIDSet)
1809 14 : bAutoFIDOnCreateViaCopy = FALSE;
1810 3755 : if (eErr == OGRERR_NONE && bAutoFIDOnCreateViaCopy)
1811 : {
1812 3720 : poFeature->SetFID(++iNextShapeId);
1813 : }
1814 : }
1815 : }
1816 : }
1817 :
1818 3882 : if (eErr == OGRERR_NONE && iFIDAsRegularColumnIndex >= 0)
1819 : {
1820 3 : poFeature->SetField(iFIDAsRegularColumnIndex, poFeature->GetFID());
1821 : }
1822 :
1823 3882 : return eErr;
1824 : }
1825 :
1826 : /************************************************************************/
1827 : /* OGRPGEscapeColumnName( ) */
1828 : /************************************************************************/
1829 :
1830 16964 : CPLString OGRPGEscapeColumnName(const char *pszColumnName)
1831 : {
1832 16964 : CPLString osStr = "\"";
1833 :
1834 16964 : char ch = '\0';
1835 212066 : for (int i = 0; (ch = pszColumnName[i]) != '\0'; i++)
1836 : {
1837 195102 : if (ch == '"')
1838 0 : osStr.append(1, ch);
1839 195102 : osStr.append(1, ch);
1840 : }
1841 :
1842 16964 : osStr += "\"";
1843 :
1844 16964 : return osStr;
1845 : }
1846 :
1847 : /************************************************************************/
1848 : /* OGRPGEscapeString( ) */
1849 : /************************************************************************/
1850 :
1851 3766 : CPLString OGRPGEscapeString(void *hPGConnIn, const char *pszStrValue,
1852 : int nMaxLength, const char *pszTableName,
1853 : const char *pszFieldName)
1854 : {
1855 3766 : PGconn *hPGConn = reinterpret_cast<PGconn *>(hPGConnIn);
1856 :
1857 3766 : size_t nSrcLen = strlen(pszStrValue);
1858 3766 : const size_t nSrcLenUTF = CPLStrlenUTF8Ex(pszStrValue);
1859 :
1860 3766 : if (nMaxLength > 0 && nSrcLenUTF > static_cast<size_t>(nMaxLength))
1861 : {
1862 1 : CPLDebug("PG", "Truncated %s.%s field value '%s' to %d characters.",
1863 : pszTableName, pszFieldName, pszStrValue, nMaxLength);
1864 :
1865 1 : size_t iUTF8Char = 0;
1866 7 : for (size_t iChar = 0; iChar < nSrcLen; iChar++)
1867 : {
1868 7 : if ((static_cast<unsigned char>(pszStrValue[iChar]) & 0xc0) != 0x80)
1869 : {
1870 6 : if (iUTF8Char == static_cast<size_t>(nMaxLength))
1871 : {
1872 1 : nSrcLen = iChar;
1873 1 : break;
1874 : }
1875 5 : iUTF8Char++;
1876 : }
1877 : }
1878 : }
1879 :
1880 3766 : char *pszDestStr = static_cast<char *>(CPLMalloc(2 * nSrcLen + 1));
1881 :
1882 3766 : CPLString osCommand;
1883 :
1884 : /* We need to quote and escape string fields. */
1885 3766 : osCommand += "'";
1886 :
1887 3766 : int nError = 0;
1888 3766 : PQescapeStringConn(hPGConn, pszDestStr, pszStrValue, nSrcLen, &nError);
1889 3766 : if (nError == 0)
1890 3766 : osCommand += pszDestStr;
1891 : else
1892 0 : CPLError(CE_Warning, CPLE_AppDefined,
1893 : "PQescapeString(): %s\n"
1894 : " input: '%s'\n"
1895 : " got: '%s'\n",
1896 : PQerrorMessage(hPGConn), pszStrValue, pszDestStr);
1897 :
1898 3766 : CPLFree(pszDestStr);
1899 :
1900 3766 : osCommand += "'";
1901 :
1902 7532 : return osCommand;
1903 : }
1904 :
1905 : /************************************************************************/
1906 : /* CreateFeatureViaInsert() */
1907 : /************************************************************************/
1908 :
1909 127 : OGRErr OGRPGTableLayer::CreateFeatureViaInsert(OGRFeature *poFeature)
1910 :
1911 : {
1912 127 : PGconn *hPGConn = poDS->GetPGConn();
1913 254 : CPLString osCommand;
1914 127 : int bNeedComma = FALSE;
1915 :
1916 127 : int bEmptyInsert = FALSE;
1917 :
1918 127 : poDS->EndCopy();
1919 :
1920 : /* -------------------------------------------------------------------- */
1921 : /* Form the INSERT command. */
1922 : /* -------------------------------------------------------------------- */
1923 127 : osCommand.Printf("INSERT INTO %s (", pszSqlTableName);
1924 :
1925 239 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
1926 : {
1927 112 : OGRGeomFieldDefn *poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(i);
1928 112 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
1929 112 : if (poGeom == nullptr)
1930 29 : continue;
1931 83 : if (!bNeedComma)
1932 83 : bNeedComma = TRUE;
1933 : else
1934 0 : osCommand += ", ";
1935 83 : osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()) + " ";
1936 : }
1937 :
1938 : /* Use case of ogr_pg_60 test */
1939 127 : if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
1940 : {
1941 7 : bNeedToUpdateSequence = true;
1942 :
1943 7 : if (bNeedComma)
1944 5 : osCommand += ", ";
1945 :
1946 7 : osCommand = osCommand + OGRPGEscapeColumnName(pszFIDColumn) + " ";
1947 7 : bNeedComma = TRUE;
1948 : }
1949 : else
1950 : {
1951 120 : UpdateSequenceIfNeeded();
1952 : }
1953 :
1954 127 : const int nFieldCount = poFeatureDefn->GetFieldCount();
1955 574 : for (int i = 0; i < nFieldCount; i++)
1956 : {
1957 447 : if (iFIDAsRegularColumnIndex == i)
1958 1 : continue;
1959 446 : if (!poFeature->IsFieldSet(i))
1960 185 : continue;
1961 261 : if (poFeature->GetDefnRef()->GetFieldDefn(i)->IsGenerated())
1962 2 : continue;
1963 :
1964 259 : if (!bNeedComma)
1965 29 : bNeedComma = TRUE;
1966 : else
1967 230 : osCommand += ", ";
1968 :
1969 : osCommand +=
1970 259 : OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1971 : }
1972 :
1973 127 : if (!bNeedComma)
1974 13 : bEmptyInsert = TRUE;
1975 :
1976 127 : osCommand += ") VALUES (";
1977 :
1978 : /* Set the geometry */
1979 127 : bNeedComma = FALSE;
1980 239 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
1981 : {
1982 : const OGRPGGeomFieldDefn *poGeomFieldDefn =
1983 112 : poFeatureDefn->GetGeomFieldDefn(i);
1984 112 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
1985 112 : if (poGeom == nullptr)
1986 29 : continue;
1987 83 : if (bNeedComma)
1988 0 : osCommand += ", ";
1989 : else
1990 83 : bNeedComma = TRUE;
1991 :
1992 83 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
1993 83 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
1994 : {
1995 54 : CheckGeomTypeCompatibility(i, poGeom);
1996 :
1997 54 : poGeom->closeRings();
1998 54 : poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
1999 54 : OGRGeometry::OGR_G_3D);
2000 54 : poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
2001 54 : OGRGeometry::OGR_G_MEASURED);
2002 :
2003 54 : int nSRSId = poGeomFieldDefn->nSRSId;
2004 :
2005 108 : char *pszHexEWKB = OGRGeometryToHexEWKB(
2006 54 : poGeom, nSRSId, poDS->sPostGISVersion.nMajor,
2007 54 : poDS->sPostGISVersion.nMinor);
2008 54 : if (!pszHexEWKB || pszHexEWKB[0] == 0)
2009 : {
2010 0 : CPLFree(pszHexEWKB);
2011 0 : return OGRERR_FAILURE;
2012 : }
2013 54 : osCommand += '\'';
2014 : try
2015 : {
2016 54 : osCommand += pszHexEWKB;
2017 : }
2018 0 : catch (const std::bad_alloc &)
2019 : {
2020 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2021 : "Out of memory: too large geometry");
2022 0 : CPLFree(pszHexEWKB);
2023 0 : return OGRERR_FAILURE;
2024 : }
2025 54 : osCommand += "'::";
2026 54 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
2027 0 : osCommand += "GEOGRAPHY";
2028 : else
2029 54 : osCommand += "GEOMETRY";
2030 54 : CPLFree(pszHexEWKB);
2031 : }
2032 29 : else if (!bWkbAsOid)
2033 : {
2034 : char *pszBytea =
2035 58 : GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
2036 29 : poDS->sPostGISVersion.nMinor);
2037 :
2038 29 : if (!pszBytea)
2039 : {
2040 0 : return OGRERR_FAILURE;
2041 : }
2042 29 : osCommand += "E'";
2043 : try
2044 : {
2045 29 : osCommand += pszBytea;
2046 : }
2047 0 : catch (const std::bad_alloc &)
2048 : {
2049 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2050 : "Out of memory: too large geometry");
2051 0 : CPLFree(pszBytea);
2052 0 : return OGRERR_FAILURE;
2053 : }
2054 29 : osCommand += '\'';
2055 29 : CPLFree(pszBytea);
2056 : }
2057 0 : else if (poGeomFieldDefn->ePostgisType ==
2058 : GEOM_TYPE_WKB /* && bWkbAsOid */)
2059 : {
2060 0 : Oid oid = GeometryToOID(poGeom);
2061 :
2062 0 : if (oid != 0)
2063 : {
2064 0 : osCommand += CPLString().Printf("'%d' ", oid);
2065 : }
2066 : else
2067 0 : osCommand += "''";
2068 : }
2069 : }
2070 :
2071 127 : if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
2072 : {
2073 7 : if (bNeedComma)
2074 5 : osCommand += ", ";
2075 7 : osCommand += CPLString().Printf(CPL_FRMT_GIB " ", poFeature->GetFID());
2076 7 : bNeedComma = TRUE;
2077 : }
2078 :
2079 574 : for (int i = 0; i < nFieldCount; i++)
2080 : {
2081 447 : if (iFIDAsRegularColumnIndex == i)
2082 1 : continue;
2083 446 : if (!poFeature->IsFieldSet(i))
2084 185 : continue;
2085 261 : if (poFeature->GetDefnRef()->GetFieldDefn(i)->IsGenerated())
2086 2 : continue;
2087 :
2088 259 : if (bNeedComma)
2089 230 : osCommand += ", ";
2090 : else
2091 29 : bNeedComma = TRUE;
2092 :
2093 259 : OGRPGCommonAppendFieldValue(osCommand, poFeature, i, OGRPGEscapeString,
2094 : hPGConn);
2095 : }
2096 :
2097 127 : osCommand += ")";
2098 :
2099 127 : if (bEmptyInsert)
2100 13 : osCommand.Printf("INSERT INTO %s DEFAULT VALUES", pszSqlTableName);
2101 :
2102 127 : int bReturnRequested = FALSE;
2103 : /* We only get the FID, but we also could add the unset fields to get */
2104 : /* the default values */
2105 247 : if (bRetrieveFID && pszFIDColumn != nullptr &&
2106 120 : poFeature->GetFID() == OGRNullFID)
2107 : {
2108 113 : if (bSkipConflicts)
2109 : {
2110 1 : CPLError(CE_Failure, CPLE_AppDefined,
2111 : "fid retrieval and skipping conflicts are not supported "
2112 : "at the same time.");
2113 1 : return OGRERR_FAILURE;
2114 : }
2115 112 : bReturnRequested = TRUE;
2116 112 : osCommand += " RETURNING ";
2117 112 : osCommand += OGRPGEscapeColumnName(pszFIDColumn);
2118 : }
2119 14 : else if (bSkipConflicts)
2120 2 : osCommand += " ON CONFLICT DO NOTHING";
2121 :
2122 : /* -------------------------------------------------------------------- */
2123 : /* Execute the insert. */
2124 : /* -------------------------------------------------------------------- */
2125 126 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2126 112 : if (bReturnRequested && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
2127 238 : PQntuples(hResult) == 1 && PQnfields(hResult) == 1)
2128 : {
2129 110 : const char *pszFID = PQgetvalue(hResult, 0, 0);
2130 110 : poFeature->SetFID(CPLAtoGIntBig(pszFID));
2131 : }
2132 16 : else if (bReturnRequested || PQresultStatus(hResult) != PGRES_COMMAND_OK)
2133 : {
2134 3 : CPLError(CE_Failure, CPLE_AppDefined,
2135 : "INSERT command for new feature failed.\n%s\nCommand: %s",
2136 6 : PQerrorMessage(hPGConn), osCommand.substr(0, 1024).c_str());
2137 :
2138 3 : if (!bHasWarnedAlreadySetFID && poFeature->GetFID() != OGRNullFID &&
2139 0 : pszFIDColumn != nullptr)
2140 : {
2141 0 : bHasWarnedAlreadySetFID = TRUE;
2142 0 : CPLError(CE_Warning, CPLE_AppDefined,
2143 : "You've inserted feature with an already set FID and "
2144 : "that's perhaps the reason for the failure. "
2145 : "If so, this can happen if you reuse the same feature "
2146 : "object for sequential insertions. "
2147 : "The FID of an inserted feature "
2148 : "is got from the server, so it is not a good idea"
2149 : "to reuse it afterwards... All in all, try unsetting the "
2150 : "FID with SetFID(-1) before calling CreateFeature()");
2151 : }
2152 :
2153 3 : OGRPGClearResult(hResult);
2154 :
2155 3 : return OGRERR_FAILURE;
2156 : }
2157 :
2158 123 : OGRPGClearResult(hResult);
2159 :
2160 123 : return OGRERR_NONE;
2161 : }
2162 :
2163 : /************************************************************************/
2164 : /* CreateFeatureViaCopy() */
2165 : /************************************************************************/
2166 :
2167 3755 : OGRErr OGRPGTableLayer::CreateFeatureViaCopy(OGRFeature *poFeature)
2168 : {
2169 3755 : PGconn *hPGConn = poDS->GetPGConn();
2170 7510 : CPLString osCommand;
2171 :
2172 : /* Tell the datasource we are now planning to copy data */
2173 3755 : poDS->StartCopy(this);
2174 :
2175 : /* First process geometry */
2176 7486 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
2177 : {
2178 : const OGRPGGeomFieldDefn *poGeomFieldDefn =
2179 3731 : poFeatureDefn->GetGeomFieldDefn(i);
2180 3731 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
2181 :
2182 3731 : char *pszGeom = nullptr;
2183 3731 : if (nullptr != poGeom)
2184 : {
2185 1664 : CheckGeomTypeCompatibility(i, poGeom);
2186 :
2187 1664 : poGeom->closeRings();
2188 1664 : poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
2189 1664 : OGRGeometry::OGR_G_3D);
2190 1664 : poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
2191 1664 : OGRGeometry::OGR_G_MEASURED);
2192 :
2193 1664 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB)
2194 797 : pszGeom = GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
2195 797 : poDS->sPostGISVersion.nMinor);
2196 : else
2197 867 : pszGeom = OGRGeometryToHexEWKB(poGeom, poGeomFieldDefn->nSRSId,
2198 867 : poDS->sPostGISVersion.nMajor,
2199 867 : poDS->sPostGISVersion.nMinor);
2200 1664 : if (!pszGeom || pszGeom[0] == 0)
2201 : {
2202 0 : CPLFree(pszGeom);
2203 0 : return OGRERR_FAILURE;
2204 : }
2205 : }
2206 :
2207 3731 : if (!osCommand.empty())
2208 12 : osCommand += "\t";
2209 :
2210 3731 : if (pszGeom)
2211 : {
2212 : try
2213 : {
2214 1664 : osCommand += pszGeom;
2215 : }
2216 0 : catch (const std::bad_alloc &)
2217 : {
2218 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2219 : "Out of memory: too large geometry");
2220 0 : CPLFree(pszGeom);
2221 0 : return OGRERR_FAILURE;
2222 : }
2223 1664 : CPLFree(pszGeom);
2224 : }
2225 : else
2226 : {
2227 2067 : osCommand += "\\N";
2228 : }
2229 : }
2230 :
2231 7510 : std::vector<bool> abFieldsToInclude(poFeature->GetFieldCount(), true);
2232 8979 : for (size_t i = 0; i < abFieldsToInclude.size(); i++)
2233 10448 : abFieldsToInclude[i] = !poFeature->GetDefnRef()
2234 5224 : ->GetFieldDefn(static_cast<int>(i))
2235 5224 : ->IsGenerated();
2236 :
2237 3755 : if (bFIDColumnInCopyFields)
2238 : {
2239 14 : OGRPGCommonAppendCopyFID(osCommand, poFeature);
2240 : }
2241 3755 : OGRPGCommonAppendCopyRegularFields(osCommand, poFeature, pszFIDColumn,
2242 : abFieldsToInclude, OGRPGEscapeString,
2243 : hPGConn);
2244 :
2245 : /* Add end of line marker */
2246 3755 : osCommand += "\n";
2247 :
2248 : // PostgreSQL doesn't provide very helpful reporting of invalid UTF-8
2249 : // content in COPY mode.
2250 7510 : if (poDS->IsUTF8ClientEncoding() &&
2251 3755 : !CPLIsUTF8(osCommand.c_str(), static_cast<int>(osCommand.size())))
2252 : {
2253 0 : CPLError(CE_Failure, CPLE_AppDefined,
2254 : "Non UTF-8 content found when writing feature " CPL_FRMT_GIB
2255 : " of layer %s: %s",
2256 0 : poFeature->GetFID(), poFeatureDefn->GetName(),
2257 : osCommand.c_str());
2258 0 : return OGRERR_FAILURE;
2259 : }
2260 :
2261 : /* ------------------------------------------------------------ */
2262 : /* Execute the copy. */
2263 : /* ------------------------------------------------------------ */
2264 :
2265 3755 : OGRErr result = OGRERR_NONE;
2266 :
2267 3755 : int copyResult = PQputCopyData(hPGConn, osCommand.c_str(),
2268 3755 : static_cast<int>(osCommand.size()));
2269 : #ifdef DEBUG_VERBOSE
2270 : CPLDebug("PG", "PQputCopyData(%s)", osCommand.c_str());
2271 : #endif
2272 :
2273 3755 : switch (copyResult)
2274 : {
2275 0 : case 0:
2276 0 : CPLError(CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
2277 0 : result = OGRERR_FAILURE;
2278 0 : break;
2279 0 : case -1:
2280 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
2281 : PQerrorMessage(hPGConn));
2282 0 : result = OGRERR_FAILURE;
2283 0 : break;
2284 : }
2285 :
2286 3755 : return result;
2287 : }
2288 :
2289 : /************************************************************************/
2290 : /* TestCapability() */
2291 : /************************************************************************/
2292 :
2293 830 : int OGRPGTableLayer::TestCapability(const char *pszCap) const
2294 :
2295 : {
2296 830 : if (bUpdateAccess)
2297 : {
2298 825 : if (EQUAL(pszCap, OLCSequentialWrite) ||
2299 818 : EQUAL(pszCap, OLCCreateField) ||
2300 806 : EQUAL(pszCap, OLCCreateGeomField) ||
2301 805 : EQUAL(pszCap, OLCDeleteField) || EQUAL(pszCap, OLCAlterFieldDefn) ||
2302 781 : EQUAL(pszCap, OLCAlterGeomFieldDefn) || EQUAL(pszCap, OLCRename))
2303 53 : return TRUE;
2304 :
2305 772 : else if (EQUAL(pszCap, OLCRandomWrite) ||
2306 765 : EQUAL(pszCap, OLCUpdateFeature) ||
2307 763 : EQUAL(pszCap, OLCDeleteFeature))
2308 : {
2309 19 : GetLayerDefn()->GetFieldCount();
2310 19 : return pszFIDColumn != nullptr;
2311 : }
2312 : }
2313 :
2314 758 : if (EQUAL(pszCap, OLCRandomRead))
2315 : {
2316 12 : GetLayerDefn()->GetFieldCount();
2317 12 : return pszFIDColumn != nullptr;
2318 : }
2319 :
2320 746 : else if (EQUAL(pszCap, OLCFastFeatureCount) ||
2321 592 : EQUAL(pszCap, OLCFastSetNextByIndex))
2322 : {
2323 185 : if (m_poFilterGeom == nullptr)
2324 159 : return TRUE;
2325 26 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
2326 26 : if (poFeatureDefn->GetGeomFieldCount() > 0)
2327 : poGeomFieldDefn =
2328 26 : poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
2329 52 : return poGeomFieldDefn == nullptr ||
2330 26 : (poDS->sPostGISVersion.nMajor >= 0 &&
2331 23 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
2332 34 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY));
2333 : }
2334 :
2335 561 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
2336 : {
2337 2 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
2338 2 : if (poFeatureDefn->GetGeomFieldCount() > 0)
2339 : poGeomFieldDefn =
2340 2 : poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
2341 4 : return poGeomFieldDefn == nullptr ||
2342 2 : (poDS->sPostGISVersion.nMajor >= 0 &&
2343 1 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
2344 2 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY));
2345 : }
2346 :
2347 559 : else if (EQUAL(pszCap, OLCTransactions))
2348 7 : return TRUE;
2349 :
2350 552 : else if (EQUAL(pszCap, OLCFastGetExtent) ||
2351 517 : EQUAL(pszCap, OLCFastGetExtent3D))
2352 : {
2353 37 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
2354 37 : if (poFeatureDefn->GetGeomFieldCount() > 0)
2355 37 : poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
2356 37 : return poGeomFieldDefn != nullptr &&
2357 70 : poDS->sPostGISVersion.nMajor >= 0 &&
2358 70 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY;
2359 : }
2360 :
2361 515 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
2362 7 : return TRUE;
2363 :
2364 508 : else if (EQUAL(pszCap, OLCCurveGeometries))
2365 248 : return TRUE;
2366 :
2367 260 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
2368 222 : return TRUE;
2369 :
2370 38 : else if (EQUAL(pszCap, OLCZGeometries))
2371 15 : return TRUE;
2372 :
2373 : else
2374 23 : return FALSE;
2375 : }
2376 :
2377 : /************************************************************************/
2378 : /* CreateField() */
2379 : /************************************************************************/
2380 :
2381 608 : OGRErr OGRPGTableLayer::CreateField(const OGRFieldDefn *poFieldIn,
2382 : int bApproxOK)
2383 :
2384 : {
2385 608 : PGconn *hPGConn = poDS->GetPGConn();
2386 1216 : CPLString osCommand;
2387 1216 : CPLString osFieldType;
2388 1216 : OGRFieldDefn oField(poFieldIn);
2389 :
2390 608 : GetLayerDefn()->GetFieldCount();
2391 :
2392 608 : if (!bUpdateAccess)
2393 : {
2394 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2395 : "CreateField");
2396 0 : return OGRERR_FAILURE;
2397 : }
2398 :
2399 610 : if (pszFIDColumn != nullptr && EQUAL(oField.GetNameRef(), pszFIDColumn) &&
2400 1218 : oField.GetType() != OFTInteger && oField.GetType() != OFTInteger64)
2401 : {
2402 1 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
2403 : oField.GetNameRef());
2404 1 : return OGRERR_FAILURE;
2405 : }
2406 :
2407 : /* -------------------------------------------------------------------- */
2408 : /* Do we want to "launder" the column names into Postgres */
2409 : /* friendly format? */
2410 : /* -------------------------------------------------------------------- */
2411 607 : if (bLaunderColumnNames)
2412 : {
2413 : char *pszSafeName =
2414 605 : OGRPGCommonLaunderName(oField.GetNameRef(), "PG", m_bUTF8ToASCII);
2415 :
2416 605 : oField.SetName(pszSafeName);
2417 605 : CPLFree(pszSafeName);
2418 :
2419 605 : if (EQUAL(oField.GetNameRef(), "oid"))
2420 : {
2421 0 : CPLError(CE_Warning, CPLE_AppDefined,
2422 : "Renaming field 'oid' to 'oid_' to avoid conflict with "
2423 : "internal oid field.");
2424 0 : oField.SetName("oid_");
2425 : }
2426 : }
2427 :
2428 : const char *pszOverrideType =
2429 607 : CSLFetchNameValue(papszOverrideColumnTypes, oField.GetNameRef());
2430 607 : if (pszOverrideType != nullptr)
2431 3 : osFieldType = pszOverrideType;
2432 : else
2433 : {
2434 1208 : osFieldType = OGRPGCommonLayerGetType(
2435 1208 : oField, CPL_TO_BOOL(bPreservePrecision), CPL_TO_BOOL(bApproxOK));
2436 604 : if (osFieldType.empty())
2437 0 : return OGRERR_FAILURE;
2438 : }
2439 :
2440 1214 : CPLString osConstraints;
2441 607 : if (!oField.IsNullable())
2442 1 : osConstraints += " NOT NULL";
2443 607 : if (oField.IsUnique())
2444 4 : osConstraints += " UNIQUE";
2445 607 : if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
2446 : {
2447 8 : osConstraints += " DEFAULT ";
2448 8 : osConstraints += OGRPGCommonLayerGetPGDefault(&oField);
2449 : }
2450 :
2451 1214 : std::string osCommentON;
2452 607 : if (!oField.GetComment().empty())
2453 : {
2454 4 : osCommentON = "COMMENT ON COLUMN ";
2455 4 : osCommentON += pszSqlTableName;
2456 4 : osCommentON += '.';
2457 4 : osCommentON += OGRPGEscapeColumnName(oField.GetNameRef());
2458 4 : osCommentON += " IS ";
2459 4 : osCommentON += OGRPGEscapeString(hPGConn, oField.GetComment().c_str());
2460 : }
2461 :
2462 : /* -------------------------------------------------------------------- */
2463 : /* Create the new field. */
2464 : /* -------------------------------------------------------------------- */
2465 607 : if (bDeferredCreation)
2466 : {
2467 582 : if (!(pszFIDColumn != nullptr &&
2468 291 : EQUAL(pszFIDColumn, oField.GetNameRef())))
2469 : {
2470 290 : osCreateTable += ", ";
2471 290 : osCreateTable += OGRPGEscapeColumnName(oField.GetNameRef());
2472 290 : osCreateTable += " ";
2473 290 : osCreateTable += osFieldType;
2474 290 : osCreateTable += osConstraints;
2475 :
2476 290 : if (!osCommentON.empty())
2477 1 : m_aosDeferredCommentOnColumns.push_back(std::move(osCommentON));
2478 : }
2479 : }
2480 : else
2481 : {
2482 316 : poDS->EndCopy();
2483 :
2484 : osCommand.Printf("ALTER TABLE %s ADD COLUMN %s %s", pszSqlTableName,
2485 632 : OGRPGEscapeColumnName(oField.GetNameRef()).c_str(),
2486 632 : osFieldType.c_str());
2487 316 : osCommand += osConstraints;
2488 :
2489 316 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2490 316 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2491 : {
2492 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2493 : PQerrorMessage(hPGConn));
2494 :
2495 0 : OGRPGClearResult(hResult);
2496 :
2497 0 : return OGRERR_FAILURE;
2498 : }
2499 :
2500 316 : OGRPGClearResult(hResult);
2501 :
2502 316 : if (!osCommentON.empty())
2503 : {
2504 3 : hResult = OGRPG_PQexec(hPGConn, osCommentON.c_str());
2505 3 : OGRPGClearResult(hResult);
2506 : }
2507 : }
2508 :
2509 607 : whileUnsealing(poFeatureDefn)->AddFieldDefn(&oField);
2510 :
2511 607 : if (pszFIDColumn != nullptr && EQUAL(oField.GetNameRef(), pszFIDColumn))
2512 : {
2513 1 : iFIDAsRegularColumnIndex = poFeatureDefn->GetFieldCount() - 1;
2514 : }
2515 :
2516 607 : return OGRERR_NONE;
2517 : }
2518 :
2519 : /************************************************************************/
2520 : /* RunAddGeometryColumn() */
2521 : /************************************************************************/
2522 :
2523 : OGRErr
2524 8 : OGRPGTableLayer::RunAddGeometryColumn(const OGRPGGeomFieldDefn *poGeomField)
2525 : {
2526 8 : PGconn *hPGConn = poDS->GetPGConn();
2527 :
2528 8 : const char *pszGeometryType = OGRToOGCGeomType(poGeomField->GetType());
2529 8 : const char *suffix = "";
2530 8 : int dim = 2;
2531 8 : if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
2532 4 : (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
2533 2 : dim = 4;
2534 6 : else if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
2535 : {
2536 1 : if (!(wkbFlatten(poGeomField->GetType()) == wkbUnknown))
2537 1 : suffix = "M";
2538 1 : dim = 3;
2539 : }
2540 5 : else if (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D)
2541 2 : dim = 3;
2542 :
2543 16 : CPLString osCommand;
2544 : osCommand.Printf(
2545 : "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
2546 16 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
2547 16 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
2548 16 : OGRPGEscapeString(hPGConn, poGeomField->GetNameRef()).c_str(),
2549 32 : poGeomField->nSRSId, pszGeometryType, suffix, dim);
2550 :
2551 8 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2552 :
2553 8 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
2554 : {
2555 0 : CPLError(CE_Failure, CPLE_AppDefined,
2556 0 : "AddGeometryColumn failed for layer %s.", GetName());
2557 :
2558 0 : OGRPGClearResult(hResult);
2559 :
2560 0 : return OGRERR_FAILURE;
2561 : }
2562 :
2563 8 : OGRPGClearResult(hResult);
2564 :
2565 8 : if (!poGeomField->IsNullable())
2566 : {
2567 : osCommand.Printf(
2568 : "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
2569 0 : OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str());
2570 :
2571 0 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2572 0 : OGRPGClearResult(hResult);
2573 : }
2574 :
2575 8 : return OGRERR_NONE;
2576 : }
2577 :
2578 : /************************************************************************/
2579 : /* RunCreateSpatialIndex() */
2580 : /************************************************************************/
2581 :
2582 : OGRErr
2583 134 : OGRPGTableLayer::RunCreateSpatialIndex(const OGRPGGeomFieldDefn *poGeomField,
2584 : int nIdx)
2585 : {
2586 134 : PGconn *hPGConn = poDS->GetPGConn();
2587 268 : CPLString osCommand;
2588 :
2589 : const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
2590 268 : pszTableName, poGeomField->GetNameRef(), nIdx));
2591 :
2592 : osCommand.Printf("CREATE INDEX %s ON %s USING %s (%s)",
2593 268 : OGRPGEscapeColumnName(osIndexName.c_str()).c_str(),
2594 : pszSqlTableName, osSpatialIndexType.c_str(),
2595 402 : OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str());
2596 :
2597 134 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2598 :
2599 134 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
2600 : {
2601 0 : CPLError(CE_Failure, CPLE_AppDefined,
2602 0 : "CREATE INDEX failed for layer %s.", GetName());
2603 :
2604 0 : OGRPGClearResult(hResult);
2605 :
2606 0 : return OGRERR_FAILURE;
2607 : }
2608 :
2609 134 : OGRPGClearResult(hResult);
2610 :
2611 134 : return OGRERR_NONE;
2612 : }
2613 :
2614 : /************************************************************************/
2615 : /* CreateGeomField() */
2616 : /************************************************************************/
2617 :
2618 18 : OGRErr OGRPGTableLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
2619 : CPL_UNUSED int bApproxOK)
2620 : {
2621 18 : OGRwkbGeometryType eType = poGeomFieldIn->GetType();
2622 18 : if (eType == wkbNone)
2623 : {
2624 0 : CPLError(CE_Failure, CPLE_AppDefined,
2625 : "Cannot create geometry field of type wkbNone");
2626 0 : return OGRERR_FAILURE;
2627 : }
2628 :
2629 : // Check if GEOMETRY_NAME layer creation option was set, but no initial
2630 : // column was created in ICreateLayer()
2631 18 : CPLString osGeomFieldName = (m_osFirstGeometryFieldName.size())
2632 2 : ? m_osFirstGeometryFieldName
2633 36 : : CPLString(poGeomFieldIn->GetNameRef());
2634 18 : m_osFirstGeometryFieldName = ""; // reset for potential next geom columns
2635 :
2636 : auto poGeomField =
2637 36 : std::make_unique<OGRPGGeomFieldDefn>(this, osGeomFieldName);
2638 18 : if (EQUAL(poGeomField->GetNameRef(), ""))
2639 : {
2640 0 : if (poFeatureDefn->GetGeomFieldCount() == 0)
2641 0 : poGeomField->SetName(EQUAL(m_osLCOGeomType.c_str(), "geography")
2642 : ? "the_geog"
2643 : : "wkb_geometry");
2644 : else
2645 0 : poGeomField->SetName(CPLSPrintf(
2646 0 : "wkb_geometry%d", poFeatureDefn->GetGeomFieldCount() + 1));
2647 : }
2648 18 : const auto poSRSIn = poGeomFieldIn->GetSpatialRef();
2649 18 : if (poSRSIn)
2650 : {
2651 4 : auto l_poSRS = poSRSIn->Clone();
2652 4 : l_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2653 4 : poGeomField->SetSpatialRef(l_poSRS);
2654 4 : l_poSRS->Release();
2655 : }
2656 : /* -------------------------------------------------------------------- */
2657 : /* Do we want to "launder" the column names into Postgres */
2658 : /* friendly format? */
2659 : /* -------------------------------------------------------------------- */
2660 18 : if (bLaunderColumnNames)
2661 : {
2662 18 : char *pszSafeName = OGRPGCommonLaunderName(poGeomField->GetNameRef(),
2663 18 : "PG", m_bUTF8ToASCII);
2664 :
2665 18 : poGeomField->SetName(pszSafeName);
2666 18 : CPLFree(pszSafeName);
2667 : }
2668 :
2669 18 : const OGRSpatialReference *poSRS = poGeomField->GetSpatialRef();
2670 18 : int nSRSId = poDS->GetUndefinedSRID();
2671 18 : if (nForcedSRSId != UNDETERMINED_SRID)
2672 0 : nSRSId = nForcedSRSId;
2673 18 : else if (poSRS != nullptr)
2674 4 : nSRSId = poDS->FetchSRSId(poSRS);
2675 :
2676 18 : int GeometryTypeFlags = 0;
2677 18 : if (OGR_GT_HasZ(eType))
2678 4 : GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
2679 18 : if (OGR_GT_HasM(eType))
2680 2 : GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
2681 18 : if (nForcedGeometryTypeFlags >= 0)
2682 : {
2683 2 : GeometryTypeFlags = nForcedGeometryTypeFlags;
2684 : eType =
2685 2 : OGR_GT_SetModifier(eType, GeometryTypeFlags & OGRGeometry::OGR_G_3D,
2686 : GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED);
2687 : }
2688 18 : poGeomField->SetType(eType);
2689 18 : poGeomField->SetNullable(poGeomFieldIn->IsNullable());
2690 18 : poGeomField->nSRSId = nSRSId;
2691 18 : poGeomField->GeometryTypeFlags = GeometryTypeFlags;
2692 36 : poGeomField->ePostgisType = EQUAL(m_osLCOGeomType.c_str(), "geography")
2693 18 : ? GEOM_TYPE_GEOGRAPHY
2694 : : GEOM_TYPE_GEOMETRY;
2695 :
2696 : /* -------------------------------------------------------------------- */
2697 : /* Create the new field. */
2698 : /* -------------------------------------------------------------------- */
2699 18 : if (!bDeferredCreation)
2700 : {
2701 8 : poDS->EndCopy();
2702 :
2703 8 : if (RunAddGeometryColumn(poGeomField.get()) != OGRERR_NONE)
2704 : {
2705 0 : return OGRERR_FAILURE;
2706 : }
2707 :
2708 8 : if (bCreateSpatialIndexFlag)
2709 : {
2710 8 : if (RunCreateSpatialIndex(poGeomField.get(), 0) != OGRERR_NONE)
2711 : {
2712 0 : return OGRERR_FAILURE;
2713 : }
2714 : }
2715 : }
2716 :
2717 18 : whileUnsealing(poFeatureDefn)->AddGeomFieldDefn(std::move(poGeomField));
2718 :
2719 18 : return OGRERR_NONE;
2720 : }
2721 :
2722 : /************************************************************************/
2723 : /* DeleteField() */
2724 : /************************************************************************/
2725 :
2726 4 : OGRErr OGRPGTableLayer::DeleteField(int iField)
2727 : {
2728 4 : PGconn *hPGConn = poDS->GetPGConn();
2729 8 : CPLString osCommand;
2730 :
2731 4 : GetLayerDefn()->GetFieldCount();
2732 :
2733 4 : if (!bUpdateAccess)
2734 : {
2735 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2736 : "DeleteField");
2737 0 : return OGRERR_FAILURE;
2738 : }
2739 :
2740 4 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
2741 : {
2742 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2743 0 : return OGRERR_FAILURE;
2744 : }
2745 :
2746 4 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
2747 0 : return OGRERR_FAILURE;
2748 4 : poDS->EndCopy();
2749 :
2750 : osCommand.Printf(
2751 : "ALTER TABLE %s DROP COLUMN %s", pszSqlTableName,
2752 8 : OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef())
2753 4 : .c_str());
2754 4 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2755 4 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2756 : {
2757 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2758 : PQerrorMessage(hPGConn));
2759 :
2760 0 : OGRPGClearResult(hResult);
2761 :
2762 0 : return OGRERR_FAILURE;
2763 : }
2764 :
2765 4 : OGRPGClearResult(hResult);
2766 :
2767 4 : return whileUnsealing(poFeatureDefn)->DeleteFieldDefn(iField);
2768 : }
2769 :
2770 : /************************************************************************/
2771 : /* AlterFieldDefn() */
2772 : /************************************************************************/
2773 :
2774 20 : OGRErr OGRPGTableLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
2775 : int nFlagsIn)
2776 : {
2777 20 : PGconn *hPGConn = poDS->GetPGConn();
2778 40 : CPLString osCommand;
2779 :
2780 20 : GetLayerDefn()->GetFieldCount();
2781 :
2782 20 : if (!bUpdateAccess)
2783 : {
2784 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2785 : "AlterFieldDefn");
2786 0 : return OGRERR_FAILURE;
2787 : }
2788 :
2789 20 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
2790 : {
2791 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2792 0 : return OGRERR_FAILURE;
2793 : }
2794 :
2795 20 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
2796 0 : return OGRERR_FAILURE;
2797 20 : poDS->EndCopy();
2798 :
2799 20 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
2800 40 : auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
2801 40 : OGRFieldDefn oField(poNewFieldDefn);
2802 :
2803 20 : poDS->SoftStartTransaction();
2804 :
2805 20 : if (!(nFlagsIn & ALTER_TYPE_FLAG))
2806 : {
2807 14 : oField.SetSubType(OFSTNone);
2808 14 : oField.SetType(poFieldDefn->GetType());
2809 14 : oField.SetSubType(poFieldDefn->GetSubType());
2810 : }
2811 :
2812 20 : if (!(nFlagsIn & ALTER_WIDTH_PRECISION_FLAG))
2813 : {
2814 14 : oField.SetWidth(poFieldDefn->GetWidth());
2815 14 : oField.SetPrecision(poFieldDefn->GetPrecision());
2816 : }
2817 :
2818 20 : if ((nFlagsIn & ALTER_TYPE_FLAG) || (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG))
2819 : {
2820 : CPLString osFieldType = OGRPGCommonLayerGetType(
2821 6 : oField, CPL_TO_BOOL(bPreservePrecision), true);
2822 6 : if (osFieldType.empty())
2823 : {
2824 0 : poDS->SoftRollbackTransaction();
2825 :
2826 0 : return OGRERR_FAILURE;
2827 : }
2828 :
2829 : osCommand.Printf(
2830 : "ALTER TABLE %s ALTER COLUMN %s TYPE %s", pszSqlTableName,
2831 12 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2832 12 : osFieldType.c_str());
2833 :
2834 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2835 6 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2836 : {
2837 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2838 : PQerrorMessage(hPGConn));
2839 :
2840 0 : OGRPGClearResult(hResult);
2841 :
2842 0 : poDS->SoftRollbackTransaction();
2843 :
2844 0 : return OGRERR_FAILURE;
2845 : }
2846 6 : OGRPGClearResult(hResult);
2847 : }
2848 :
2849 26 : if ((nFlagsIn & ALTER_NULLABLE_FLAG) &&
2850 6 : poFieldDefn->IsNullable() != poNewFieldDefn->IsNullable())
2851 : {
2852 2 : oField.SetNullable(poNewFieldDefn->IsNullable());
2853 :
2854 2 : if (poNewFieldDefn->IsNullable())
2855 : osCommand.Printf(
2856 : "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", pszSqlTableName,
2857 1 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2858 : else
2859 : osCommand.Printf(
2860 : "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
2861 1 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2862 :
2863 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2864 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2865 : {
2866 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2867 : PQerrorMessage(hPGConn));
2868 :
2869 0 : OGRPGClearResult(hResult);
2870 :
2871 0 : poDS->SoftRollbackTransaction();
2872 :
2873 0 : return OGRERR_FAILURE;
2874 : }
2875 2 : OGRPGClearResult(hResult);
2876 : }
2877 :
2878 : // Only supports adding a unique constraint
2879 30 : if ((nFlagsIn & ALTER_UNIQUE_FLAG) && !poFieldDefn->IsUnique() &&
2880 10 : poNewFieldDefn->IsUnique())
2881 : {
2882 2 : oField.SetUnique(poNewFieldDefn->IsUnique());
2883 :
2884 : osCommand.Printf(
2885 : "ALTER TABLE %s ADD UNIQUE (%s)", pszSqlTableName,
2886 2 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2887 :
2888 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2889 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2890 : {
2891 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2892 : PQerrorMessage(hPGConn));
2893 :
2894 0 : OGRPGClearResult(hResult);
2895 :
2896 0 : poDS->SoftRollbackTransaction();
2897 :
2898 0 : return OGRERR_FAILURE;
2899 : }
2900 2 : OGRPGClearResult(hResult);
2901 : }
2902 22 : else if ((nFlagsIn & ALTER_UNIQUE_FLAG) && poFieldDefn->IsUnique() &&
2903 4 : !poNewFieldDefn->IsUnique())
2904 : {
2905 2 : oField.SetUnique(TRUE);
2906 2 : CPLError(CE_Warning, CPLE_NotSupported,
2907 : "Dropping a UNIQUE constraint is not supported currently");
2908 : }
2909 :
2910 28 : if ((nFlagsIn & ALTER_DEFAULT_FLAG) &&
2911 8 : ((poFieldDefn->GetDefault() == nullptr &&
2912 6 : poNewFieldDefn->GetDefault() != nullptr) ||
2913 8 : (poFieldDefn->GetDefault() != nullptr &&
2914 2 : poNewFieldDefn->GetDefault() == nullptr) ||
2915 7 : (poFieldDefn->GetDefault() != nullptr &&
2916 1 : poNewFieldDefn->GetDefault() != nullptr &&
2917 1 : strcmp(poFieldDefn->GetDefault(), poNewFieldDefn->GetDefault()) !=
2918 : 0)))
2919 : {
2920 2 : oField.SetDefault(poNewFieldDefn->GetDefault());
2921 :
2922 2 : if (poNewFieldDefn->GetDefault() == nullptr)
2923 : osCommand.Printf(
2924 : "ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT", pszSqlTableName,
2925 1 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2926 : else
2927 : osCommand.Printf(
2928 : "ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s",
2929 : pszSqlTableName,
2930 2 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2931 3 : OGRPGCommonLayerGetPGDefault(poNewFieldDefn).c_str());
2932 :
2933 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2934 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2935 : {
2936 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2937 : PQerrorMessage(hPGConn));
2938 :
2939 0 : OGRPGClearResult(hResult);
2940 :
2941 0 : poDS->SoftRollbackTransaction();
2942 :
2943 0 : return OGRERR_FAILURE;
2944 : }
2945 2 : OGRPGClearResult(hResult);
2946 : }
2947 :
2948 30 : if ((nFlagsIn & ALTER_COMMENT_FLAG) &&
2949 10 : poFieldDefn->GetComment() != poNewFieldDefn->GetComment())
2950 : {
2951 6 : oField.SetComment(poNewFieldDefn->GetComment());
2952 :
2953 6 : if (!poNewFieldDefn->GetComment().empty())
2954 : {
2955 : osCommand.Printf(
2956 : "COMMENT ON COLUMN %s.%s IS %s", pszSqlTableName,
2957 8 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2958 8 : OGRPGEscapeString(hPGConn, poNewFieldDefn->GetComment().c_str())
2959 8 : .c_str());
2960 : }
2961 : else
2962 : {
2963 : osCommand.Printf(
2964 : "COMMENT ON COLUMN %s.%s IS NULL", pszSqlTableName,
2965 2 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2966 : }
2967 :
2968 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2969 6 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2970 : {
2971 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2972 : PQerrorMessage(hPGConn));
2973 :
2974 0 : OGRPGClearResult(hResult);
2975 :
2976 0 : poDS->SoftRollbackTransaction();
2977 :
2978 0 : return OGRERR_FAILURE;
2979 : }
2980 6 : OGRPGClearResult(hResult);
2981 : }
2982 :
2983 20 : if ((nFlagsIn & ALTER_NAME_FLAG))
2984 : {
2985 6 : if (bLaunderColumnNames)
2986 : {
2987 6 : char *pszSafeName = OGRPGCommonLaunderName(oField.GetNameRef(),
2988 6 : "PG", m_bUTF8ToASCII);
2989 6 : oField.SetName(pszSafeName);
2990 6 : CPLFree(pszSafeName);
2991 : }
2992 :
2993 6 : if (EQUAL(oField.GetNameRef(), "oid"))
2994 : {
2995 0 : CPLError(CE_Warning, CPLE_AppDefined,
2996 : "Renaming field 'oid' to 'oid_' to avoid conflict with "
2997 : "internal oid field.");
2998 0 : oField.SetName("oid_");
2999 : }
3000 :
3001 6 : if (strcmp(poFieldDefn->GetNameRef(), oField.GetNameRef()) != 0)
3002 : {
3003 : osCommand.Printf(
3004 : "ALTER TABLE %s RENAME COLUMN %s TO %s", pszSqlTableName,
3005 12 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
3006 18 : OGRPGEscapeColumnName(oField.GetNameRef()).c_str());
3007 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3008 6 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3009 : {
3010 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s",
3011 : osCommand.c_str(), PQerrorMessage(hPGConn));
3012 :
3013 0 : OGRPGClearResult(hResult);
3014 :
3015 0 : poDS->SoftRollbackTransaction();
3016 :
3017 0 : return OGRERR_FAILURE;
3018 : }
3019 6 : OGRPGClearResult(hResult);
3020 : }
3021 : }
3022 :
3023 20 : poDS->SoftCommitTransaction();
3024 :
3025 20 : if (nFlagsIn & ALTER_NAME_FLAG)
3026 6 : poFieldDefn->SetName(oField.GetNameRef());
3027 20 : if (nFlagsIn & ALTER_TYPE_FLAG)
3028 : {
3029 6 : poFieldDefn->SetSubType(OFSTNone);
3030 6 : poFieldDefn->SetType(oField.GetType());
3031 6 : poFieldDefn->SetSubType(oField.GetSubType());
3032 : }
3033 20 : if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
3034 : {
3035 6 : poFieldDefn->SetWidth(oField.GetWidth());
3036 6 : poFieldDefn->SetPrecision(oField.GetPrecision());
3037 : }
3038 20 : if (nFlagsIn & ALTER_NULLABLE_FLAG)
3039 6 : poFieldDefn->SetNullable(oField.IsNullable());
3040 20 : if (nFlagsIn & ALTER_DEFAULT_FLAG)
3041 8 : poFieldDefn->SetDefault(oField.GetDefault());
3042 20 : if (nFlagsIn & ALTER_UNIQUE_FLAG)
3043 14 : poFieldDefn->SetUnique(oField.IsUnique());
3044 20 : if (nFlagsIn & ALTER_COMMENT_FLAG)
3045 10 : poFieldDefn->SetComment(oField.GetComment());
3046 :
3047 20 : return OGRERR_NONE;
3048 : }
3049 :
3050 : /************************************************************************/
3051 : /* AlterGeomFieldDefn() */
3052 : /************************************************************************/
3053 :
3054 : OGRErr
3055 6 : OGRPGTableLayer::AlterGeomFieldDefn(int iGeomFieldToAlter,
3056 : const OGRGeomFieldDefn *poNewGeomFieldDefn,
3057 : int nFlagsIn)
3058 : {
3059 6 : PGconn *hPGConn = poDS->GetPGConn();
3060 12 : CPLString osCommand;
3061 :
3062 6 : if (!bUpdateAccess)
3063 : {
3064 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3065 : "AlterGeomFieldDefn");
3066 0 : return OGRERR_FAILURE;
3067 : }
3068 :
3069 11 : if (iGeomFieldToAlter < 0 ||
3070 5 : iGeomFieldToAlter >= GetLayerDefn()->GetGeomFieldCount())
3071 : {
3072 1 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
3073 1 : return OGRERR_FAILURE;
3074 : }
3075 :
3076 5 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3077 0 : return OGRERR_FAILURE;
3078 5 : poDS->EndCopy();
3079 :
3080 5 : auto poGeomFieldDefn = cpl::down_cast<OGRPGGeomFieldDefn *>(
3081 5 : poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter));
3082 10 : auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
3083 :
3084 5 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG)
3085 : {
3086 5 : const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
3087 5 : if (poNewSRSRef && poNewSRSRef->GetCoordinateEpoch() > 0)
3088 : {
3089 1 : CPLError(CE_Failure, CPLE_NotSupported,
3090 : "Setting a coordinate epoch is not supported for "
3091 : "PostGIS");
3092 1 : return OGRERR_FAILURE;
3093 : }
3094 : }
3095 :
3096 8 : const OGRGeomFieldDefn oGeomField(poNewGeomFieldDefn);
3097 4 : poDS->SoftStartTransaction();
3098 :
3099 4 : int nGeomTypeFlags = poGeomFieldDefn->GeometryTypeFlags;
3100 :
3101 8 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG) &&
3102 4 : poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
3103 : {
3104 : const char *pszGeometryType =
3105 2 : OGRToOGCGeomType(poNewGeomFieldDefn->GetType());
3106 2 : std::string osType;
3107 2 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
3108 2 : osType += "geometry(";
3109 : else
3110 0 : osType += "geography(";
3111 2 : osType += pszGeometryType;
3112 2 : nGeomTypeFlags = 0;
3113 2 : if (OGR_GT_HasZ(poNewGeomFieldDefn->GetType()))
3114 0 : nGeomTypeFlags |= OGRGeometry::OGR_G_3D;
3115 2 : if (OGR_GT_HasM(poNewGeomFieldDefn->GetType()))
3116 0 : nGeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
3117 2 : if (nGeomTypeFlags & OGRGeometry::OGR_G_3D)
3118 0 : osType += "Z";
3119 2 : else if (nGeomTypeFlags & OGRGeometry::OGR_G_MEASURED)
3120 0 : osType += "M";
3121 2 : if (poGeomFieldDefn->nSRSId > 0)
3122 2 : osType += CPLSPrintf(",%d", poGeomFieldDefn->nSRSId);
3123 2 : osType += ")";
3124 :
3125 : osCommand.Printf(
3126 : "ALTER TABLE %s ALTER COLUMN %s TYPE %s", pszSqlTableName,
3127 4 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
3128 4 : osType.c_str());
3129 :
3130 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3131 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3132 : {
3133 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3134 : PQerrorMessage(hPGConn));
3135 :
3136 1 : OGRPGClearResult(hResult);
3137 :
3138 1 : poDS->SoftRollbackTransaction();
3139 :
3140 1 : return OGRERR_FAILURE;
3141 : }
3142 1 : OGRPGClearResult(hResult);
3143 : }
3144 :
3145 3 : const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
3146 3 : int nSRID = poGeomFieldDefn->nSRSId;
3147 :
3148 3 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG))
3149 : {
3150 3 : const auto poNewSRS = poNewGeomFieldDefn->GetSpatialRef();
3151 3 : const char *const apszOptions[] = {
3152 : "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
3153 3 : if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
3154 7 : (poOldSRS != nullptr && poNewSRS == nullptr) ||
3155 1 : (poOldSRS != nullptr && poNewSRS != nullptr &&
3156 1 : !poOldSRS->IsSame(poNewSRS, apszOptions)))
3157 : {
3158 3 : if (poNewSRS)
3159 2 : nSRID = poDS->FetchSRSId(poNewSRS);
3160 : else
3161 1 : nSRID = 0;
3162 :
3163 : osCommand.Printf(
3164 : "SELECT UpdateGeometrySRID(%s,%s,%s,%d)",
3165 6 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
3166 6 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
3167 6 : OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef())
3168 : .c_str(),
3169 9 : nSRID);
3170 :
3171 3 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3172 3 : if (PQresultStatus(hResult) != PGRES_TUPLES_OK)
3173 : {
3174 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s",
3175 : osCommand.c_str(), PQerrorMessage(hPGConn));
3176 :
3177 0 : OGRPGClearResult(hResult);
3178 :
3179 0 : poDS->SoftRollbackTransaction();
3180 :
3181 0 : return OGRERR_FAILURE;
3182 : }
3183 3 : OGRPGClearResult(hResult);
3184 : }
3185 : }
3186 :
3187 6 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG) &&
3188 3 : poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
3189 : {
3190 2 : if (poNewGeomFieldDefn->IsNullable())
3191 : osCommand.Printf(
3192 : "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", pszSqlTableName,
3193 1 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str());
3194 : else
3195 : osCommand.Printf(
3196 : "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
3197 1 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str());
3198 :
3199 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3200 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3201 : {
3202 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3203 : PQerrorMessage(hPGConn));
3204 :
3205 0 : OGRPGClearResult(hResult);
3206 :
3207 0 : poDS->SoftRollbackTransaction();
3208 :
3209 0 : return OGRERR_FAILURE;
3210 : }
3211 2 : OGRPGClearResult(hResult);
3212 : }
3213 :
3214 6 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) &&
3215 3 : strcmp(poGeomFieldDefn->GetNameRef(),
3216 : poNewGeomFieldDefn->GetNameRef()) != 0)
3217 : {
3218 : osCommand.Printf(
3219 : "ALTER TABLE %s RENAME COLUMN %s TO %s", pszSqlTableName,
3220 2 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
3221 3 : OGRPGEscapeColumnName(oGeomField.GetNameRef()).c_str());
3222 1 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3223 1 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3224 : {
3225 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3226 : PQerrorMessage(hPGConn));
3227 :
3228 0 : OGRPGClearResult(hResult);
3229 :
3230 0 : poDS->SoftRollbackTransaction();
3231 :
3232 0 : return OGRERR_FAILURE;
3233 : }
3234 1 : OGRPGClearResult(hResult);
3235 : }
3236 :
3237 3 : poDS->SoftCommitTransaction();
3238 :
3239 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG)
3240 3 : poGeomFieldDefn->SetName(oGeomField.GetNameRef());
3241 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
3242 : {
3243 3 : poGeomFieldDefn->GeometryTypeFlags = nGeomTypeFlags;
3244 3 : poGeomFieldDefn->SetType(oGeomField.GetType());
3245 : }
3246 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
3247 3 : poGeomFieldDefn->SetNullable(oGeomField.IsNullable());
3248 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG)
3249 : {
3250 3 : const auto poSRSRef = oGeomField.GetSpatialRef();
3251 3 : if (poSRSRef)
3252 : {
3253 2 : auto poSRSNew = poSRSRef->Clone();
3254 2 : poGeomFieldDefn->SetSpatialRef(poSRSNew);
3255 2 : poSRSNew->Release();
3256 : }
3257 : else
3258 : {
3259 1 : poGeomFieldDefn->SetSpatialRef(nullptr);
3260 : }
3261 3 : poGeomFieldDefn->nSRSId = nSRID;
3262 : }
3263 :
3264 3 : return OGRERR_NONE;
3265 : }
3266 :
3267 : /************************************************************************/
3268 : /* GetFeature() */
3269 : /************************************************************************/
3270 :
3271 146 : OGRFeature *OGRPGTableLayer::GetFeature(GIntBig nFeatureId)
3272 :
3273 : {
3274 146 : GetLayerDefn()->GetFieldCount();
3275 :
3276 146 : if (pszFIDColumn == nullptr)
3277 3 : return OGRLayer::GetFeature(nFeatureId);
3278 :
3279 : /* -------------------------------------------------------------------- */
3280 : /* Issue query for a single record. */
3281 : /* -------------------------------------------------------------------- */
3282 143 : OGRFeature *poFeature = nullptr;
3283 143 : PGconn *hPGConn = poDS->GetPGConn();
3284 286 : CPLString osFieldList = BuildFields();
3285 143 : CPLString osCommand;
3286 :
3287 143 : poDS->EndCopy();
3288 143 : poDS->SoftStartTransaction();
3289 :
3290 : osCommand.Printf("DECLARE getfeaturecursor %s for "
3291 : "SELECT %s FROM %s WHERE %s = " CPL_FRMT_GIB,
3292 143 : (poDS->bUseBinaryCursor) ? "BINARY CURSOR" : "CURSOR",
3293 : osFieldList.c_str(), pszSqlTableName,
3294 143 : OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFeatureId);
3295 :
3296 143 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
3297 :
3298 143 : if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
3299 : {
3300 143 : OGRPGClearResult(hResult);
3301 :
3302 143 : hResult = OGRPG_PQexec(hPGConn, "FETCH ALL in getfeaturecursor");
3303 :
3304 143 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
3305 : {
3306 143 : int nRows = PQntuples(hResult);
3307 143 : if (nRows > 0)
3308 : {
3309 121 : int *panTempMapFieldNameToIndex = nullptr;
3310 121 : int *panTempMapFieldNameToGeomIndex = nullptr;
3311 121 : CreateMapFromFieldNameToIndex(hResult, poFeatureDefn,
3312 : panTempMapFieldNameToIndex,
3313 : panTempMapFieldNameToGeomIndex);
3314 121 : poFeature = RecordToFeature(hResult, panTempMapFieldNameToIndex,
3315 : panTempMapFieldNameToGeomIndex, 0);
3316 121 : CPLFree(panTempMapFieldNameToIndex);
3317 121 : CPLFree(panTempMapFieldNameToGeomIndex);
3318 121 : if (poFeature && iFIDAsRegularColumnIndex >= 0)
3319 : {
3320 1 : poFeature->SetField(iFIDAsRegularColumnIndex,
3321 : poFeature->GetFID());
3322 : }
3323 :
3324 121 : if (nRows > 1)
3325 : {
3326 0 : CPLError(
3327 : CE_Warning, CPLE_AppDefined,
3328 : "%d rows in response to the WHERE %s = " CPL_FRMT_GIB
3329 : " clause !",
3330 : nRows, pszFIDColumn, nFeatureId);
3331 : }
3332 : }
3333 : else
3334 : {
3335 22 : CPLError(CE_Failure, CPLE_AppDefined,
3336 : "Attempt to read feature with unknown feature id "
3337 : "(" CPL_FRMT_GIB ").",
3338 : nFeatureId);
3339 : }
3340 : }
3341 : }
3342 0 : else if (hResult && PQresultStatus(hResult) == PGRES_FATAL_ERROR)
3343 : {
3344 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
3345 : PQresultErrorMessage(hResult));
3346 : }
3347 :
3348 : /* -------------------------------------------------------------------- */
3349 : /* Cleanup */
3350 : /* -------------------------------------------------------------------- */
3351 143 : OGRPGClearResult(hResult);
3352 :
3353 143 : hResult = OGRPG_PQexec(hPGConn, "CLOSE getfeaturecursor");
3354 143 : OGRPGClearResult(hResult);
3355 :
3356 143 : poDS->SoftCommitTransaction();
3357 :
3358 143 : return poFeature;
3359 : }
3360 :
3361 : /************************************************************************/
3362 : /* GetFeatureCount() */
3363 : /************************************************************************/
3364 :
3365 152 : GIntBig OGRPGTableLayer::GetFeatureCount(int bForce)
3366 :
3367 : {
3368 152 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3369 0 : return 0;
3370 152 : poDS->EndCopy();
3371 :
3372 152 : if (TestCapability(OLCFastFeatureCount) == FALSE)
3373 11 : return OGRPGLayer::GetFeatureCount(bForce);
3374 :
3375 : /* -------------------------------------------------------------------- */
3376 : /* In theory it might be wise to cache this result, but it */
3377 : /* won't be trivial to work out the lifetime of the value. */
3378 : /* After all someone else could be adding records from another */
3379 : /* application when working against a database. */
3380 : /* -------------------------------------------------------------------- */
3381 141 : PGconn *hPGConn = poDS->GetPGConn();
3382 141 : CPLString osCommand;
3383 141 : GIntBig nCount = 0;
3384 :
3385 : osCommand.Printf("SELECT count(*) FROM %s %s", pszSqlTableName,
3386 141 : osWHERE.c_str());
3387 :
3388 141 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3389 141 : if (hResult != nullptr && PQresultStatus(hResult) == PGRES_TUPLES_OK)
3390 141 : nCount = CPLAtoGIntBig(PQgetvalue(hResult, 0, 0));
3391 : else
3392 0 : CPLDebug("PG", "%s; failed.", osCommand.c_str());
3393 141 : OGRPGClearResult(hResult);
3394 :
3395 141 : return nCount;
3396 : }
3397 :
3398 : /************************************************************************/
3399 : /* ResolveSRID() */
3400 : /************************************************************************/
3401 :
3402 87 : void OGRPGTableLayer::ResolveSRID(const OGRPGGeomFieldDefn *poGFldDefn)
3403 :
3404 : {
3405 87 : PGconn *hPGConn = poDS->GetPGConn();
3406 87 : CPLString osCommand;
3407 :
3408 87 : int nSRSId = poDS->GetUndefinedSRID();
3409 87 : if (!poDS->m_bHasGeometryColumns)
3410 : {
3411 0 : poGFldDefn->nSRSId = nSRSId;
3412 0 : return;
3413 : }
3414 :
3415 : osCommand.Printf(
3416 : "SELECT srid FROM geometry_columns "
3417 : "WHERE f_table_name = %s AND "
3418 : "f_geometry_column = %s",
3419 174 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
3420 261 : OGRPGEscapeString(hPGConn, poGFldDefn->GetNameRef()).c_str());
3421 :
3422 : osCommand +=
3423 87 : CPLString().Printf(" AND f_table_schema = %s",
3424 87 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
3425 :
3426 87 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
3427 :
3428 174 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
3429 87 : PQntuples(hResult) == 1)
3430 : {
3431 39 : nSRSId = atoi(PQgetvalue(hResult, 0, 0));
3432 : }
3433 :
3434 87 : OGRPGClearResult(hResult);
3435 :
3436 : /* With PostGIS 2.0, SRID = 0 can also mean that there's no constraint */
3437 : /* so we need to fetch from values */
3438 : /* We assume that all geometry of this column have identical SRID */
3439 87 : if (nSRSId <= 0 && poGFldDefn->ePostgisType == GEOM_TYPE_GEOMETRY &&
3440 39 : poDS->sPostGISVersion.nMajor >= 0)
3441 : {
3442 72 : CPLString osGetSRID;
3443 36 : osGetSRID += "SELECT ST_SRID(";
3444 36 : osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef());
3445 36 : osGetSRID += ") FROM ";
3446 36 : osGetSRID += pszSqlTableName;
3447 36 : osGetSRID += " WHERE (";
3448 36 : osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef());
3449 36 : osGetSRID += " IS NOT NULL) LIMIT 1";
3450 :
3451 36 : hResult = OGRPG_PQexec(poDS->GetPGConn(), osGetSRID);
3452 72 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
3453 36 : PQntuples(hResult) == 1)
3454 : {
3455 34 : nSRSId = atoi(PQgetvalue(hResult, 0, 0));
3456 : }
3457 :
3458 36 : OGRPGClearResult(hResult);
3459 : }
3460 :
3461 87 : poGFldDefn->nSRSId = nSRSId;
3462 : }
3463 :
3464 : /************************************************************************/
3465 : /* StartCopy() */
3466 : /************************************************************************/
3467 :
3468 193 : OGRErr OGRPGTableLayer::StartCopy()
3469 :
3470 : {
3471 : /*CPLDebug("PG", "OGRPGDataSource(%p)::StartCopy(%p)", poDS, this);*/
3472 :
3473 193 : CPLString osFields = BuildCopyFields();
3474 :
3475 193 : size_t size = osFields.size() + strlen(pszSqlTableName) + 100;
3476 193 : char *pszCommand = static_cast<char *>(CPLMalloc(size));
3477 :
3478 193 : snprintf(pszCommand, size, "COPY %s (%s) FROM STDIN;", pszSqlTableName,
3479 : osFields.c_str());
3480 :
3481 193 : PGconn *hPGConn = poDS->GetPGConn();
3482 193 : PGresult *hResult = OGRPG_PQexec(hPGConn, pszCommand);
3483 :
3484 193 : if (!hResult || (PQresultStatus(hResult) != PGRES_COPY_IN))
3485 : {
3486 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
3487 : }
3488 : else
3489 193 : bCopyActive = TRUE;
3490 :
3491 193 : OGRPGClearResult(hResult);
3492 193 : CPLFree(pszCommand);
3493 :
3494 386 : return OGRERR_NONE;
3495 : }
3496 :
3497 : /************************************************************************/
3498 : /* EndCopy() */
3499 : /************************************************************************/
3500 :
3501 193 : OGRErr OGRPGTableLayer::EndCopy()
3502 :
3503 : {
3504 193 : if (!bCopyActive)
3505 0 : return OGRERR_NONE;
3506 : /*CPLDebug("PG", "OGRPGDataSource(%p)::EndCopy(%p)", poDS, this);*/
3507 :
3508 : /* This method is called from the datasource when
3509 : a COPY operation is ended */
3510 193 : OGRErr result = OGRERR_NONE;
3511 :
3512 193 : PGconn *hPGConn = poDS->GetPGConn();
3513 193 : CPLDebug("PG", "PQputCopyEnd()");
3514 :
3515 193 : bCopyActive = FALSE;
3516 :
3517 193 : int copyResult = PQputCopyEnd(hPGConn, nullptr);
3518 :
3519 193 : switch (copyResult)
3520 : {
3521 0 : case 0:
3522 0 : CPLError(CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
3523 0 : result = OGRERR_FAILURE;
3524 0 : break;
3525 0 : case -1:
3526 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
3527 : PQerrorMessage(hPGConn));
3528 0 : result = OGRERR_FAILURE;
3529 0 : break;
3530 : }
3531 :
3532 : /* Now check the results of the copy */
3533 193 : PGresult *hResult = PQgetResult(hPGConn);
3534 :
3535 193 : if (hResult && PQresultStatus(hResult) != PGRES_COMMAND_OK)
3536 : {
3537 1 : CPLError(CE_Failure, CPLE_AppDefined, "COPY statement failed.\n%s",
3538 : PQerrorMessage(hPGConn));
3539 :
3540 1 : result = OGRERR_FAILURE;
3541 : }
3542 :
3543 193 : OGRPGClearResult(hResult);
3544 :
3545 193 : if (!bUseCopyByDefault)
3546 34 : bUseCopy = USE_COPY_UNSET;
3547 :
3548 193 : UpdateSequenceIfNeeded();
3549 :
3550 193 : return result;
3551 : }
3552 :
3553 : /************************************************************************/
3554 : /* UpdateSequenceIfNeeded() */
3555 : /************************************************************************/
3556 :
3557 1067 : void OGRPGTableLayer::UpdateSequenceIfNeeded()
3558 : {
3559 1067 : if (bNeedToUpdateSequence && pszFIDColumn != nullptr)
3560 : {
3561 18 : PGconn *hPGConn = poDS->GetPGConn();
3562 18 : CPLString osCommand;
3563 : // setval() only works if the value is in [1,INT_MAX] range
3564 : // so do not update it if MAX(fid) <= 0
3565 : osCommand.Printf(
3566 : "SELECT setval(pg_get_serial_sequence(%s, %s), MAX(%s)) FROM %s "
3567 : "WHERE EXISTS (SELECT 1 FROM %s WHERE %s > 0 LIMIT 1)",
3568 36 : OGRPGEscapeString(hPGConn, pszSqlTableName).c_str(),
3569 36 : OGRPGEscapeString(hPGConn, pszFIDColumn).c_str(),
3570 18 : OGRPGEscapeColumnName(pszFIDColumn).c_str(), pszSqlTableName,
3571 72 : pszSqlTableName, OGRPGEscapeColumnName(pszFIDColumn).c_str());
3572 18 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3573 18 : OGRPGClearResult(hResult);
3574 18 : bNeedToUpdateSequence = false;
3575 : }
3576 1067 : }
3577 :
3578 : /************************************************************************/
3579 : /* BuildCopyFields() */
3580 : /************************************************************************/
3581 :
3582 193 : CPLString OGRPGTableLayer::BuildCopyFields()
3583 : {
3584 193 : int i = 0;
3585 193 : int nFIDIndex = -1;
3586 193 : CPLString osFieldList;
3587 :
3588 370 : for (i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
3589 : {
3590 177 : OGRGeomFieldDefn *poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(i);
3591 177 : if (!osFieldList.empty())
3592 6 : osFieldList += ", ";
3593 177 : osFieldList += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
3594 : }
3595 :
3596 193 : if (bFIDColumnInCopyFields)
3597 : {
3598 11 : if (!osFieldList.empty())
3599 7 : osFieldList += ", ";
3600 :
3601 11 : nFIDIndex = poFeatureDefn->GetFieldIndex(pszFIDColumn);
3602 :
3603 11 : osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
3604 : }
3605 :
3606 716 : for (i = 0; i < poFeatureDefn->GetFieldCount(); i++)
3607 : {
3608 523 : if (i == nFIDIndex)
3609 2 : continue;
3610 :
3611 521 : if (poFeatureDefn->GetFieldDefn(i)->IsGenerated())
3612 2 : continue;
3613 :
3614 519 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
3615 :
3616 519 : if (!osFieldList.empty())
3617 501 : osFieldList += ", ";
3618 :
3619 519 : osFieldList += OGRPGEscapeColumnName(pszName);
3620 : }
3621 :
3622 193 : return osFieldList;
3623 : }
3624 :
3625 : /************************************************************************/
3626 : /* CheckGeomTypeCompatibility() */
3627 : /************************************************************************/
3628 :
3629 1718 : void OGRPGTableLayer::CheckGeomTypeCompatibility(int iGeomField,
3630 : OGRGeometry *poGeom)
3631 : {
3632 1718 : if (bHasWarnedIncompatibleGeom)
3633 0 : return;
3634 :
3635 : OGRwkbGeometryType eExpectedGeomType =
3636 1718 : poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetType();
3637 1718 : OGRwkbGeometryType eFlatLayerGeomType = wkbFlatten(eExpectedGeomType);
3638 1718 : OGRwkbGeometryType eFlatGeomType = wkbFlatten(poGeom->getGeometryType());
3639 1718 : if (eFlatLayerGeomType == wkbUnknown)
3640 692 : return;
3641 :
3642 1026 : if (eFlatLayerGeomType == wkbGeometryCollection)
3643 0 : bHasWarnedIncompatibleGeom = eFlatGeomType != wkbMultiPoint &&
3644 0 : eFlatGeomType != wkbMultiLineString &&
3645 0 : eFlatGeomType != wkbMultiPolygon &&
3646 : eFlatGeomType != wkbGeometryCollection;
3647 : else
3648 1026 : bHasWarnedIncompatibleGeom = (eFlatGeomType != eFlatLayerGeomType);
3649 :
3650 1026 : if (bHasWarnedIncompatibleGeom)
3651 : {
3652 2 : CPLError(CE_Warning, CPLE_AppDefined,
3653 : "Geometry to be inserted is of type %s, whereas the layer "
3654 : "geometry type is %s.\n"
3655 : "Insertion is likely to fail",
3656 1 : OGRGeometryTypeToName(poGeom->getGeometryType()),
3657 : OGRGeometryTypeToName(eExpectedGeomType));
3658 : }
3659 : }
3660 :
3661 : /************************************************************************/
3662 : /* SetOverrideColumnTypes() */
3663 : /************************************************************************/
3664 :
3665 234 : void OGRPGTableLayer::SetOverrideColumnTypes(const char *pszOverrideColumnTypes)
3666 : {
3667 234 : if (pszOverrideColumnTypes == nullptr)
3668 233 : return;
3669 :
3670 1 : const char *pszIter = pszOverrideColumnTypes;
3671 2 : CPLString osCur;
3672 44 : while (*pszIter != '\0')
3673 : {
3674 44 : if (*pszIter == '(')
3675 : {
3676 : /* Ignore commas inside ( ) pair */
3677 12 : while (*pszIter != '\0')
3678 : {
3679 12 : if (*pszIter == ')')
3680 : {
3681 2 : osCur += *pszIter;
3682 2 : pszIter++;
3683 2 : break;
3684 : }
3685 10 : osCur += *pszIter;
3686 10 : pszIter++;
3687 : }
3688 2 : if (*pszIter == '\0')
3689 1 : break;
3690 : }
3691 :
3692 43 : if (*pszIter == ',')
3693 : {
3694 3 : papszOverrideColumnTypes =
3695 3 : CSLAddString(papszOverrideColumnTypes, osCur);
3696 3 : osCur = "";
3697 : }
3698 : else
3699 40 : osCur += *pszIter;
3700 43 : pszIter++;
3701 : }
3702 1 : if (!osCur.empty())
3703 1 : papszOverrideColumnTypes =
3704 1 : CSLAddString(papszOverrideColumnTypes, osCur);
3705 : }
3706 :
3707 : /************************************************************************/
3708 : /* IGetExtent() */
3709 : /* */
3710 : /* For PostGIS use internal ST_EstimatedExtent(geometry) function */
3711 : /* if bForce == 0 */
3712 : /************************************************************************/
3713 :
3714 22 : OGRErr OGRPGTableLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
3715 : bool bForce)
3716 : {
3717 44 : CPLString osCommand;
3718 :
3719 22 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3720 0 : return OGRERR_FAILURE;
3721 22 : poDS->EndCopy();
3722 :
3723 : OGRPGGeomFieldDefn *poGeomFieldDefn =
3724 22 : poFeatureDefn->GetGeomFieldDefn(iGeomField);
3725 :
3726 : // if bForce is 0 and ePostgisType is not GEOM_TYPE_GEOGRAPHY we can use
3727 : // the ST_EstimatedExtent function which is quicker
3728 : // ST_EstimatedExtent was called ST_Estimated_Extent up to PostGIS 2.0.x
3729 : // ST_EstimatedExtent returns NULL in absence of statistics (an exception
3730 : // before
3731 : // PostGIS 1.5.4)
3732 22 : if (bForce == 0 && TestCapability(OLCFastGetExtent))
3733 : {
3734 1 : PGconn *hPGConn = poDS->GetPGConn();
3735 :
3736 2 : const char *pszExtentFct = poDS->sPostGISVersion.nMajor > 2 ||
3737 0 : (poDS->sPostGISVersion.nMajor == 2 &&
3738 0 : poDS->sPostGISVersion.nMinor >= 1)
3739 1 : ? "ST_EstimatedExtent"
3740 : : "ST_Estimated_Extent";
3741 :
3742 : osCommand.Printf(
3743 : "SELECT %s(%s, %s, %s)", pszExtentFct,
3744 2 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
3745 2 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
3746 4 : OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef()).c_str());
3747 :
3748 : /* Quiet error: ST_Estimated_Extent may return an error if statistics */
3749 : /* have not been computed */
3750 1 : if (RunGetExtentRequest(*psExtent, bForce, osCommand, TRUE) ==
3751 : OGRERR_NONE)
3752 1 : return OGRERR_NONE;
3753 :
3754 0 : CPLDebug(
3755 : "PG",
3756 : "Unable to get estimated extent by PostGIS. Trying real extent.");
3757 : }
3758 :
3759 21 : return OGRPGLayer::IGetExtent(iGeomField, psExtent, bForce);
3760 : }
3761 :
3762 : /************************************************************************/
3763 : /* Rename() */
3764 : /************************************************************************/
3765 :
3766 6 : OGRErr OGRPGTableLayer::Rename(const char *pszNewName)
3767 : {
3768 6 : if (!TestCapability(OLCRename))
3769 0 : return OGRERR_FAILURE;
3770 :
3771 6 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3772 0 : return OGRERR_FAILURE;
3773 6 : poDS->EndCopy();
3774 6 : ResetReading();
3775 :
3776 6 : char *pszNewSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszNewName));
3777 6 : PGconn *hPGConn = poDS->GetPGConn();
3778 6 : CPLString osCommand;
3779 : osCommand.Printf("ALTER TABLE %s RENAME TO %s", pszSqlTableName,
3780 6 : pszNewSqlTableName);
3781 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3782 :
3783 6 : OGRErr eRet = OGRERR_NONE;
3784 6 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
3785 : {
3786 2 : eRet = OGRERR_FAILURE;
3787 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
3788 :
3789 2 : CPLFree(pszNewSqlTableName);
3790 : }
3791 : else
3792 : {
3793 4 : CPLFree(pszTableName);
3794 4 : pszTableName = CPLStrdup(pszNewName);
3795 :
3796 4 : CPLFree(pszSqlTableName);
3797 4 : pszSqlTableName = pszNewSqlTableName;
3798 :
3799 4 : SetDescription(pszNewName);
3800 4 : whileUnsealing(poFeatureDefn)->SetName(pszNewName);
3801 : }
3802 :
3803 6 : OGRPGClearResult(hResult);
3804 :
3805 6 : return eRet;
3806 : }
3807 :
3808 : /************************************************************************/
3809 : /* SetDeferredCreation() */
3810 : /************************************************************************/
3811 :
3812 234 : void OGRPGTableLayer::SetDeferredCreation(int bDeferredCreationIn,
3813 : const std::string &osCreateTableIn)
3814 : {
3815 234 : bDeferredCreation = bDeferredCreationIn;
3816 234 : osCreateTable = osCreateTableIn;
3817 234 : }
3818 :
3819 : /************************************************************************/
3820 : /* RunDeferredCreationIfNecessary() */
3821 : /************************************************************************/
3822 :
3823 1553 : OGRErr OGRPGTableLayer::RunDeferredCreationIfNecessary()
3824 : {
3825 1553 : if (!bDeferredCreation)
3826 1416 : return OGRERR_NONE;
3827 137 : bDeferredCreation = FALSE;
3828 :
3829 137 : poDS->EndCopy();
3830 :
3831 263 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
3832 : {
3833 126 : OGRPGGeomFieldDefn *poGeomField = poFeatureDefn->GetGeomFieldDefn(i);
3834 :
3835 126 : if (poDS->HavePostGIS() ||
3836 0 : poGeomField->ePostgisType == GEOM_TYPE_GEOGRAPHY)
3837 : {
3838 : const char *pszGeometryType =
3839 126 : OGRToOGCGeomType(poGeomField->GetType());
3840 :
3841 126 : osCreateTable += ", ";
3842 126 : osCreateTable += OGRPGEscapeColumnName(poGeomField->GetNameRef());
3843 126 : osCreateTable += " ";
3844 126 : if (poGeomField->ePostgisType == GEOM_TYPE_GEOMETRY)
3845 120 : osCreateTable += "geometry(";
3846 : else
3847 6 : osCreateTable += "geography(";
3848 126 : osCreateTable += pszGeometryType;
3849 126 : if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
3850 46 : (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
3851 3 : osCreateTable += "ZM";
3852 123 : else if (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D)
3853 43 : osCreateTable += "Z";
3854 80 : else if (poGeomField->GeometryTypeFlags &
3855 : OGRGeometry::OGR_G_MEASURED)
3856 4 : osCreateTable += "M";
3857 126 : if (poGeomField->nSRSId > 0)
3858 20 : osCreateTable += CPLSPrintf(",%d", poGeomField->nSRSId);
3859 126 : osCreateTable += ")";
3860 126 : if (!poGeomField->IsNullable())
3861 1 : osCreateTable += " NOT NULL";
3862 : }
3863 : }
3864 :
3865 137 : osCreateTable += " )";
3866 274 : CPLString osCommand(osCreateTable);
3867 :
3868 137 : PGconn *hPGConn = poDS->GetPGConn();
3869 :
3870 137 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
3871 137 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3872 : {
3873 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3874 : PQerrorMessage(hPGConn));
3875 :
3876 0 : OGRPGClearResult(hResult);
3877 0 : return OGRERR_FAILURE;
3878 : }
3879 :
3880 137 : OGRPGClearResult(hResult);
3881 :
3882 138 : for (const auto &osSQL : m_aosDeferredCommentOnColumns)
3883 : {
3884 1 : hResult = OGRPG_PQexec(hPGConn, osSQL.c_str());
3885 1 : OGRPGClearResult(hResult);
3886 : }
3887 137 : m_aosDeferredCommentOnColumns.clear();
3888 :
3889 137 : if (bCreateSpatialIndexFlag)
3890 : {
3891 263 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
3892 : {
3893 : OGRPGGeomFieldDefn *poGeomField =
3894 126 : poFeatureDefn->GetGeomFieldDefn(i);
3895 126 : if (RunCreateSpatialIndex(poGeomField, i) != OGRERR_NONE)
3896 : {
3897 0 : return OGRERR_FAILURE;
3898 : }
3899 : }
3900 : }
3901 :
3902 137 : CSLConstList papszMD = OGRLayer::GetMetadata();
3903 137 : if (papszMD != nullptr)
3904 2 : SetMetadata(papszMD);
3905 :
3906 137 : return OGRERR_NONE;
3907 : }
3908 :
3909 : /************************************************************************/
3910 : /* GetGeometryTypes() */
3911 : /************************************************************************/
3912 :
3913 27 : OGRGeometryTypeCounter *OGRPGTableLayer::GetGeometryTypes(
3914 : int iGeomField, int nFlagsGGT, int &nEntryCountOut,
3915 : GDALProgressFunc pfnProgress, void *pProgressData)
3916 : {
3917 27 : if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount())
3918 : {
3919 1 : CPLError(CE_Failure, CPLE_AppDefined,
3920 : "Invalid geometry field index : %d", iGeomField);
3921 1 : nEntryCountOut = 0;
3922 1 : return nullptr;
3923 : }
3924 :
3925 26 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3926 : {
3927 0 : nEntryCountOut = 0;
3928 0 : return nullptr;
3929 : }
3930 26 : poDS->EndCopy();
3931 :
3932 : const OGRPGGeomFieldDefn *poGeomFieldDefn =
3933 26 : GetLayerDefn()->GetGeomFieldDefn(iGeomField);
3934 : const auto osEscapedGeom =
3935 52 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
3936 52 : CPLString osSQL;
3937 26 : if ((nFlagsGGT & OGR_GGT_GEOMCOLLECTIONZ_TINZ) != 0)
3938 : {
3939 18 : CPLString osFilter;
3940 : osFilter.Printf("(ST_Zmflag(%s) = 2 AND "
3941 : "((GeometryType(%s) = 'GEOMETRYCOLLECTION' AND "
3942 : "ST_NumGeometries(%s) >= 1 AND "
3943 : "geometrytype(ST_GeometryN(%s, 1)) = 'TIN') OR "
3944 : "GeometryType(%s) = 'TIN'))",
3945 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3946 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3947 9 : osEscapedGeom.c_str());
3948 :
3949 18 : std::string l_osWHERE(osWHERE);
3950 9 : if (l_osWHERE.empty())
3951 6 : l_osWHERE = " WHERE ";
3952 : else
3953 3 : l_osWHERE += " AND ";
3954 9 : l_osWHERE += "(NOT (";
3955 9 : l_osWHERE += osFilter;
3956 9 : l_osWHERE += ") OR ";
3957 9 : l_osWHERE += osEscapedGeom;
3958 9 : l_osWHERE += " IS NULL)";
3959 :
3960 18 : std::string l_osWHEREFilter(osWHERE);
3961 9 : if (l_osWHEREFilter.empty())
3962 6 : l_osWHEREFilter = " WHERE ";
3963 : else
3964 3 : l_osWHEREFilter += " AND ";
3965 9 : l_osWHEREFilter += osFilter;
3966 :
3967 : osSQL.Printf(
3968 : "(SELECT GeometryType(%s), ST_Zmflag(%s), COUNT(*) FROM %s %s "
3969 : "GROUP BY GeometryType(%s), ST_Zmflag(%s)) UNION ALL "
3970 : "(SELECT * FROM (SELECT 'TIN', 2, COUNT(*) AS count FROM %s %s) "
3971 : "tinsubselect WHERE tinsubselect.count != 0)",
3972 : osEscapedGeom.c_str(), osEscapedGeom.c_str(), pszSqlTableName,
3973 : l_osWHERE.c_str(), osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3974 9 : pszSqlTableName, l_osWHEREFilter.c_str());
3975 : }
3976 17 : else if ((nFlagsGGT & OGR_GGT_STOP_IF_MIXED) != 0)
3977 : {
3978 8 : std::string l_osWHERE(osWHERE);
3979 4 : if (l_osWHERE.empty())
3980 2 : l_osWHERE = " WHERE ";
3981 : else
3982 2 : l_osWHERE += " AND ";
3983 4 : l_osWHERE += osEscapedGeom;
3984 4 : l_osWHERE += " IS NOT NULL";
3985 :
3986 8 : std::string l_osWHERE_NULL(osWHERE);
3987 4 : if (l_osWHERE_NULL.empty())
3988 2 : l_osWHERE_NULL = " WHERE ";
3989 : else
3990 2 : l_osWHERE_NULL += " AND ";
3991 4 : l_osWHERE_NULL += osEscapedGeom;
3992 4 : l_osWHERE_NULL += " IS NULL";
3993 :
3994 : osSQL.Printf("(SELECT DISTINCT GeometryType(%s), ST_Zmflag(%s), 0 FROM "
3995 : "%s %s LIMIT 2) "
3996 : "UNION ALL (SELECT NULL, NULL, 0 FROM %s %s LIMIT 1)",
3997 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3998 : pszSqlTableName, l_osWHERE.c_str(), pszSqlTableName,
3999 4 : l_osWHERE_NULL.c_str());
4000 : }
4001 : else
4002 : {
4003 : const bool bDebug =
4004 13 : CPLTestBool(CPLGetConfigOption("OGR_PG_DEBUG_GGT_CANCEL", "NO"));
4005 : osSQL.Printf(
4006 : "SELECT GeometryType(%s), ST_Zmflag(%s), COUNT(*)%s FROM %s %s "
4007 : "GROUP BY GeometryType(%s), ST_Zmflag(%s)",
4008 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
4009 : bDebug ? ", pg_sleep(1)" : "", pszSqlTableName, osWHERE.c_str(),
4010 13 : osEscapedGeom.c_str(), osEscapedGeom.c_str());
4011 : }
4012 :
4013 52 : std::thread thread;
4014 26 : std::mutex mutex;
4015 26 : std::condition_variable cv;
4016 26 : bool stopThread = false;
4017 26 : if (pfnProgress && pfnProgress != GDALDummyProgress)
4018 : {
4019 4 : thread = std::thread(
4020 2 : [&]()
4021 : {
4022 4 : std::unique_lock<std::mutex> lock(mutex);
4023 4 : while (!stopThread)
4024 : {
4025 2 : if (!pfnProgress(0.0, "", pProgressData))
4026 1 : poDS->AbortSQL();
4027 2 : cv.wait_for(lock, std::chrono::milliseconds(100));
4028 : }
4029 4 : });
4030 : }
4031 :
4032 26 : PGconn *hPGConn = poDS->GetPGConn();
4033 26 : PGresult *hResult = OGRPG_PQexec(hPGConn, osSQL.c_str());
4034 :
4035 26 : if (pfnProgress && pfnProgress != GDALDummyProgress)
4036 : {
4037 : {
4038 4 : std::unique_lock<std::mutex> lock(mutex);
4039 2 : stopThread = true;
4040 2 : cv.notify_one();
4041 : }
4042 2 : thread.join();
4043 : }
4044 :
4045 26 : nEntryCountOut = 0;
4046 26 : OGRGeometryTypeCounter *pasRet = nullptr;
4047 26 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
4048 : {
4049 25 : const int nTuples = PQntuples(hResult);
4050 25 : nEntryCountOut = nTuples;
4051 : pasRet = static_cast<OGRGeometryTypeCounter *>(
4052 25 : CPLCalloc(1 + nEntryCountOut, sizeof(OGRGeometryTypeCounter)));
4053 69 : for (int i = 0; i < nTuples; ++i)
4054 : {
4055 44 : const char *pszGeomType = PQgetvalue(hResult, i, 0);
4056 44 : const char *pszZMFlag = PQgetvalue(hResult, i, 1);
4057 44 : const char *pszCount = PQgetvalue(hResult, i, 2);
4058 44 : if (pszCount)
4059 : {
4060 44 : if (pszGeomType == nullptr || pszGeomType[0] == '\0')
4061 : {
4062 16 : pasRet[i].eGeomType = wkbNone;
4063 : }
4064 28 : else if (pszZMFlag != nullptr)
4065 : {
4066 28 : const int nZMFlag = atoi(pszZMFlag);
4067 28 : pasRet[i].eGeomType = OGRFromOGCGeomType(pszGeomType);
4068 28 : int nModifier = 0;
4069 28 : if (nZMFlag == 1)
4070 1 : nModifier = OGRGeometry::OGR_G_MEASURED;
4071 27 : else if (nZMFlag == 2)
4072 9 : nModifier = OGRGeometry::OGR_G_3D;
4073 18 : else if (nZMFlag == 3)
4074 1 : nModifier =
4075 : OGRGeometry::OGR_G_MEASURED | OGRGeometry::OGR_G_3D;
4076 28 : pasRet[i].eGeomType = OGR_GT_SetModifier(
4077 28 : pasRet[i].eGeomType, nModifier & OGRGeometry::OGR_G_3D,
4078 : nModifier & OGRGeometry::OGR_G_MEASURED);
4079 : }
4080 44 : pasRet[i].nCount =
4081 44 : static_cast<int64_t>(std::strtoll(pszCount, nullptr, 10));
4082 : }
4083 : }
4084 : }
4085 :
4086 26 : OGRPGClearResult(hResult);
4087 :
4088 26 : return pasRet;
4089 : }
4090 :
4091 : /************************************************************************/
4092 : /* FindFieldIndex() */
4093 : /************************************************************************/
4094 :
4095 24 : int OGRPGTableLayer::FindFieldIndex(const char *pszFieldName, int bExactMatch)
4096 : {
4097 24 : const auto poLayerDefn = GetLayerDefn();
4098 24 : int iField = poLayerDefn->GetFieldIndex(pszFieldName);
4099 :
4100 24 : if (!bExactMatch && iField < 0 && bLaunderColumnNames)
4101 : {
4102 10 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4103 : char *pszSafeName =
4104 5 : OGRPGCommonLaunderName(pszFieldName, "PG", m_bUTF8ToASCII);
4105 5 : iField = poLayerDefn->GetFieldIndex(pszSafeName);
4106 5 : CPLFree(pszSafeName);
4107 : }
4108 :
4109 24 : return iField;
4110 : }
4111 :
4112 : #undef PQexec
|