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