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 133358 : virtual int GetFieldCount() const override
59 : {
60 133358 : SolveFields();
61 133358 : return OGRPGFeatureDefn::GetFieldCount();
62 : }
63 :
64 50788 : virtual OGRFieldDefn *GetFieldDefn(int i) override
65 : {
66 50788 : SolveFields();
67 50788 : 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 56449 : virtual int GetGeomFieldCount() const override
83 : {
84 56449 : if (poLayer != nullptr && !poLayer->HasGeometryInformation())
85 1178 : SolveFields();
86 56449 : return OGRPGFeatureDefn::GetGeomFieldCount();
87 : }
88 :
89 14643 : virtual OGRPGGeomFieldDefn *GetGeomFieldDefn(int i) override
90 : {
91 14643 : if (poLayer != nullptr && !poLayer->HasGeometryInformation())
92 162 : SolveFields();
93 14643 : 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 229777 : void OGRPGTableFeatureDefn::SolveFields() const
116 : {
117 229777 : if (poLayer == nullptr)
118 0 : return;
119 :
120 229777 : 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 230277 : int OGRPGTableLayer::ReadTableDefinition()
589 :
590 : {
591 230277 : PGconn *hPGConn = poDS->GetPGConn();
592 :
593 230277 : if (bTableDefinitionValid >= 0)
594 229825 : 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 751 : oField.SetGenerated(pszGenerated != nullptr && pszGenerated[0] != '\0');
831 :
832 : // CPLDebug("PG", "name=%s, type=%s", oField.GetNameRef(), pszType);
833 751 : poFeatureDefn->AddFieldDefn(&oField);
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 : /* ISetSpatialFilter() */
1012 : /************************************************************************/
1013 :
1014 82 : OGRErr OGRPGTableLayer::ISetSpatialFilter(int iGeomField,
1015 : const OGRGeometry *poGeomIn)
1016 :
1017 : {
1018 82 : m_iGeomFieldFilter = iGeomField;
1019 :
1020 82 : if (InstallFilter(poGeomIn))
1021 : {
1022 41 : BuildWhere();
1023 :
1024 41 : ResetReading();
1025 : }
1026 :
1027 82 : return OGRERR_NONE;
1028 : }
1029 :
1030 : /************************************************************************/
1031 : /* BuildWhere() */
1032 : /* */
1033 : /* Build the WHERE statement appropriate to the current set of */
1034 : /* criteria (spatial and attribute queries). */
1035 : /************************************************************************/
1036 :
1037 207 : void OGRPGTableLayer::BuildWhere()
1038 :
1039 : {
1040 207 : osWHERE = "";
1041 207 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
1042 207 : if (poFeatureDefn->GetGeomFieldCount() != 0)
1043 203 : poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
1044 :
1045 207 : if (m_poFilterGeom != nullptr && poGeomFieldDefn != nullptr &&
1046 53 : poDS->sPostGISVersion.nMajor >= 0 &&
1047 49 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
1048 18 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY))
1049 : {
1050 : char szBox3D_1[128];
1051 : char szBox3D_2[128];
1052 31 : OGREnvelope sEnvelope;
1053 :
1054 31 : m_poFilterGeom->getEnvelope(&sEnvelope);
1055 31 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1056 : {
1057 0 : if (sEnvelope.MinX < -180.0)
1058 0 : sEnvelope.MinX = -180.0;
1059 0 : if (sEnvelope.MinY < -90.0)
1060 0 : sEnvelope.MinY = -90.0;
1061 0 : if (sEnvelope.MaxX > 180.0)
1062 0 : sEnvelope.MaxX = 180.0;
1063 0 : if (sEnvelope.MaxY > 90.0)
1064 0 : sEnvelope.MaxY = 90.0;
1065 : }
1066 31 : CPLsnprintf(szBox3D_1, sizeof(szBox3D_1), "%.17g %.17g", sEnvelope.MinX,
1067 : sEnvelope.MinY);
1068 31 : CPLsnprintf(szBox3D_2, sizeof(szBox3D_2), "%.17g %.17g", sEnvelope.MaxX,
1069 : sEnvelope.MaxY);
1070 : osWHERE.Printf(
1071 : "WHERE %s && ST_SetSRID('BOX3D(%s, %s)'::box3d,%d) ",
1072 62 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
1073 62 : szBox3D_1, szBox3D_2, poGeomFieldDefn->nSRSId);
1074 : }
1075 :
1076 207 : if (!osQuery.empty())
1077 : {
1078 89 : if (osWHERE.empty())
1079 : {
1080 82 : osWHERE.Printf("WHERE %s ", osQuery.c_str());
1081 : }
1082 : else
1083 : {
1084 7 : osWHERE += "AND (";
1085 7 : osWHERE += osQuery;
1086 7 : osWHERE += ")";
1087 : }
1088 : }
1089 207 : }
1090 :
1091 : /************************************************************************/
1092 : /* BuildFullQueryStatement() */
1093 : /************************************************************************/
1094 :
1095 994 : void OGRPGTableLayer::BuildFullQueryStatement()
1096 :
1097 : {
1098 994 : CPLString osFields = BuildFields();
1099 994 : if (pszQueryStatement != nullptr)
1100 : {
1101 586 : CPLFree(pszQueryStatement);
1102 586 : pszQueryStatement = nullptr;
1103 : }
1104 1988 : pszQueryStatement = static_cast<char *>(CPLMalloc(
1105 994 : osFields.size() + osWHERE.size() + strlen(pszSqlTableName) + 40));
1106 994 : snprintf(pszQueryStatement,
1107 994 : osFields.size() + osWHERE.size() + strlen(pszSqlTableName) + 40,
1108 : "SELECT %s FROM %s %s", osFields.c_str(), pszSqlTableName,
1109 : osWHERE.c_str());
1110 994 : }
1111 :
1112 : /************************************************************************/
1113 : /* ResetReading() */
1114 : /************************************************************************/
1115 :
1116 1025 : void OGRPGTableLayer::ResetReading()
1117 :
1118 : {
1119 1025 : if (bInResetReading)
1120 31 : return;
1121 994 : bInResetReading = TRUE;
1122 :
1123 994 : if (bDeferredCreation)
1124 15 : RunDeferredCreationIfNecessary();
1125 994 : poDS->EndCopy();
1126 994 : bUseCopyByDefault = FALSE;
1127 :
1128 994 : BuildFullQueryStatement();
1129 :
1130 994 : OGRPGLayer::ResetReading();
1131 :
1132 994 : bInResetReading = FALSE;
1133 : }
1134 :
1135 : /************************************************************************/
1136 : /* GetNextFeature() */
1137 : /************************************************************************/
1138 :
1139 1211 : OGRFeature *OGRPGTableLayer::GetNextFeature()
1140 :
1141 : {
1142 1211 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1143 0 : return nullptr;
1144 1211 : poDS->EndCopy();
1145 :
1146 1211 : if (pszQueryStatement == nullptr)
1147 6 : ResetReading();
1148 :
1149 1211 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
1150 1211 : if (poFeatureDefn->GetGeomFieldCount() != 0)
1151 1139 : poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
1152 1211 : poFeatureDefn->GetFieldCount();
1153 :
1154 : while (true)
1155 : {
1156 1307 : OGRFeature *poFeature = GetNextRawFeature();
1157 1307 : if (poFeature == nullptr)
1158 122 : return nullptr;
1159 :
1160 : /* We just have to look if there is a geometry filter */
1161 : /* If there's a PostGIS geometry column, the spatial filter */
1162 : /* is already taken into account in the select request */
1163 : /* The attribute filter is always taken into account by the select
1164 : * request */
1165 341 : if (m_poFilterGeom == nullptr || poGeomFieldDefn == nullptr ||
1166 341 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
1167 1730 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
1168 204 : FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter)))
1169 : {
1170 1089 : if (iFIDAsRegularColumnIndex >= 0)
1171 : {
1172 1 : poFeature->SetField(iFIDAsRegularColumnIndex,
1173 : poFeature->GetFID());
1174 : }
1175 1089 : return poFeature;
1176 : }
1177 :
1178 96 : delete poFeature;
1179 96 : }
1180 : }
1181 :
1182 : /************************************************************************/
1183 : /* BuildFields() */
1184 : /* */
1185 : /* Build list of fields to fetch, performing any required */
1186 : /* transformations (such as on geometry). */
1187 : /************************************************************************/
1188 :
1189 1137 : CPLString OGRPGTableLayer::BuildFields()
1190 :
1191 : {
1192 1137 : int i = 0;
1193 1137 : CPLString osFieldList;
1194 :
1195 1137 : poFeatureDefn->GetFieldCount();
1196 :
1197 2231 : if (pszFIDColumn != nullptr &&
1198 1094 : poFeatureDefn->GetFieldIndex(pszFIDColumn) == -1)
1199 : {
1200 1090 : osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
1201 : }
1202 :
1203 2184 : for (i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
1204 : {
1205 : OGRPGGeomFieldDefn *poGeomFieldDefn =
1206 1047 : poFeatureDefn->GetGeomFieldDefn(i);
1207 : CPLString osEscapedGeom =
1208 2094 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
1209 :
1210 1047 : if (!osFieldList.empty())
1211 1020 : osFieldList += ", ";
1212 :
1213 1047 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
1214 : {
1215 624 : if (!poDS->HavePostGIS() || poDS->bUseBinaryCursor)
1216 : {
1217 8 : osFieldList += osEscapedGeom;
1218 : }
1219 616 : else if (CPLTestBool(CPLGetConfigOption("PG_USE_BASE64", "NO")))
1220 : {
1221 1 : osFieldList += "encode(ST_AsEWKB(";
1222 1 : osFieldList += osEscapedGeom;
1223 1 : osFieldList += "), 'base64') AS ";
1224 2 : osFieldList += OGRPGEscapeColumnName(
1225 1 : CPLSPrintf("EWKBBase64_%s", poGeomFieldDefn->GetNameRef()));
1226 : }
1227 : else
1228 : {
1229 : /* This will return EWKB in an hex encoded form */
1230 615 : osFieldList += osEscapedGeom;
1231 : }
1232 : }
1233 423 : else if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1234 : {
1235 : #if defined(BINARY_CURSOR_ENABLED)
1236 : if (poDS->bUseBinaryCursor)
1237 : {
1238 : osFieldList += "ST_AsBinary(";
1239 : osFieldList += osEscapedGeom;
1240 : osFieldList += ") AS";
1241 : osFieldList += OGRPGEscapeColumnName(
1242 : CPLSPrintf("AsBinary_%s", poGeomFieldDefn->GetNameRef()));
1243 : }
1244 : else
1245 : #endif
1246 15 : if (CPLTestBool(CPLGetConfigOption("PG_USE_BASE64", "NO")))
1247 : {
1248 0 : osFieldList += "encode(ST_AsEWKB(";
1249 0 : osFieldList += osEscapedGeom;
1250 0 : osFieldList += "::geometry), 'base64') AS ";
1251 0 : osFieldList += OGRPGEscapeColumnName(
1252 0 : CPLSPrintf("EWKBBase64_%s", poGeomFieldDefn->GetNameRef()));
1253 : }
1254 : else
1255 : {
1256 15 : osFieldList += osEscapedGeom;
1257 : }
1258 : }
1259 : else
1260 : {
1261 408 : osFieldList += osEscapedGeom;
1262 : }
1263 : }
1264 :
1265 5331 : for (i = 0; i < poFeatureDefn->GetFieldCount(); i++)
1266 : {
1267 4194 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
1268 :
1269 4194 : if (!osFieldList.empty())
1270 4174 : osFieldList += ", ";
1271 :
1272 : #if defined(BINARY_CURSOR_ENABLED)
1273 : /* With a binary cursor, it is not possible to get the time zone */
1274 : /* of a timestamptz column. So we fallback to asking it in text mode */
1275 : if (poDS->bUseBinaryCursor &&
1276 : poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
1277 : {
1278 : osFieldList += "CAST (";
1279 : osFieldList += OGRPGEscapeColumnName(pszName);
1280 : osFieldList += " AS text)";
1281 : }
1282 : else
1283 : #endif
1284 : {
1285 4194 : osFieldList += OGRPGEscapeColumnName(pszName);
1286 : }
1287 : }
1288 :
1289 1137 : return osFieldList;
1290 : }
1291 :
1292 : /************************************************************************/
1293 : /* SetAttributeFilter() */
1294 : /************************************************************************/
1295 :
1296 166 : OGRErr OGRPGTableLayer::SetAttributeFilter(const char *pszQuery)
1297 :
1298 : {
1299 166 : CPLFree(m_pszAttrQueryString);
1300 166 : m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : nullptr;
1301 :
1302 166 : if (pszQuery == nullptr)
1303 77 : osQuery = "";
1304 : else
1305 89 : osQuery = pszQuery;
1306 :
1307 166 : BuildWhere();
1308 :
1309 166 : ResetReading();
1310 :
1311 166 : return OGRERR_NONE;
1312 : }
1313 :
1314 : /************************************************************************/
1315 : /* DeleteFeature() */
1316 : /************************************************************************/
1317 :
1318 26 : OGRErr OGRPGTableLayer::DeleteFeature(GIntBig nFID)
1319 :
1320 : {
1321 26 : PGconn *hPGConn = poDS->GetPGConn();
1322 52 : CPLString osCommand;
1323 :
1324 26 : GetLayerDefn()->GetFieldCount();
1325 :
1326 26 : if (!bUpdateAccess)
1327 : {
1328 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1329 : "DeleteFeature");
1330 0 : return OGRERR_FAILURE;
1331 : }
1332 :
1333 26 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1334 0 : return OGRERR_FAILURE;
1335 26 : poDS->EndCopy();
1336 26 : bAutoFIDOnCreateViaCopy = FALSE;
1337 :
1338 : /* -------------------------------------------------------------------- */
1339 : /* We can only delete features if we have a well defined FID */
1340 : /* column to target. */
1341 : /* -------------------------------------------------------------------- */
1342 26 : if (pszFIDColumn == nullptr)
1343 : {
1344 0 : CPLError(CE_Failure, CPLE_AppDefined,
1345 : "DeleteFeature(" CPL_FRMT_GIB
1346 : ") failed. Unable to delete features in tables without\n"
1347 : "a recognised FID column.",
1348 : nFID);
1349 0 : return OGRERR_FAILURE;
1350 : }
1351 :
1352 : /* -------------------------------------------------------------------- */
1353 : /* Form the statement to drop the record. */
1354 : /* -------------------------------------------------------------------- */
1355 : osCommand.Printf("DELETE FROM %s WHERE %s = " CPL_FRMT_GIB, pszSqlTableName,
1356 26 : OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFID);
1357 :
1358 : /* -------------------------------------------------------------------- */
1359 : /* Execute the delete. */
1360 : /* -------------------------------------------------------------------- */
1361 : OGRErr eErr;
1362 :
1363 26 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
1364 :
1365 26 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1366 : {
1367 0 : CPLError(CE_Failure, CPLE_AppDefined,
1368 : "DeleteFeature() DELETE statement failed.\n%s",
1369 : PQerrorMessage(hPGConn));
1370 :
1371 0 : eErr = OGRERR_FAILURE;
1372 : }
1373 : else
1374 : {
1375 26 : if (EQUAL(PQcmdStatus(hResult), "DELETE 0"))
1376 12 : eErr = OGRERR_NON_EXISTING_FEATURE;
1377 : else
1378 14 : eErr = OGRERR_NONE;
1379 : }
1380 :
1381 26 : OGRPGClearResult(hResult);
1382 :
1383 26 : return eErr;
1384 : }
1385 :
1386 : /************************************************************************/
1387 : /* ISetFeature() */
1388 : /* */
1389 : /* SetFeature() is implemented by an UPDATE SQL command */
1390 : /************************************************************************/
1391 :
1392 2053 : OGRErr OGRPGTableLayer::ISetFeature(OGRFeature *poFeature)
1393 :
1394 : {
1395 2053 : return IUpdateFeature(poFeature, -1, nullptr, -1, nullptr, false);
1396 : }
1397 :
1398 : /************************************************************************/
1399 : /* UpdateFeature() */
1400 : /************************************************************************/
1401 :
1402 2071 : OGRErr OGRPGTableLayer::IUpdateFeature(OGRFeature *poFeature,
1403 : int nUpdatedFieldsCount,
1404 : const int *panUpdatedFieldsIdx,
1405 : int nUpdatedGeomFieldsCount,
1406 : const int *panUpdatedGeomFieldsIdx,
1407 : bool /* bUpdateStyleString*/)
1408 :
1409 : {
1410 2071 : PGconn *hPGConn = poDS->GetPGConn();
1411 4142 : CPLString osCommand;
1412 2071 : bool bNeedComma = false;
1413 2071 : OGRErr eErr = OGRERR_FAILURE;
1414 :
1415 2071 : GetLayerDefn()->GetFieldCount();
1416 :
1417 2071 : const char *pszMethodName =
1418 2071 : nUpdatedFieldsCount >= 0 ? "UpdateFeature" : "SetFeature";
1419 2071 : if (!bUpdateAccess)
1420 : {
1421 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1422 : pszMethodName);
1423 0 : return OGRERR_FAILURE;
1424 : }
1425 :
1426 2071 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1427 0 : return OGRERR_FAILURE;
1428 2071 : poDS->EndCopy();
1429 :
1430 2071 : if (nullptr == poFeature)
1431 : {
1432 0 : CPLError(CE_Failure, CPLE_AppDefined,
1433 : "NULL pointer to OGRFeature passed to %s().", pszMethodName);
1434 0 : return eErr;
1435 : }
1436 :
1437 2071 : if (poFeature->GetFID() == OGRNullFID)
1438 : {
1439 2 : CPLError(CE_Failure, CPLE_AppDefined,
1440 : "FID required on features given to %s().", pszMethodName);
1441 2 : return eErr;
1442 : }
1443 :
1444 2069 : if (pszFIDColumn == nullptr)
1445 : {
1446 0 : CPLError(CE_Failure, CPLE_AppDefined,
1447 : "Unable to update features in tables without\n"
1448 : "a recognised FID column.");
1449 0 : return eErr;
1450 : }
1451 :
1452 : /* In case the FID column has also been created as a regular field */
1453 2069 : if (iFIDAsRegularColumnIndex >= 0)
1454 : {
1455 5 : if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex) ||
1456 2 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
1457 2 : poFeature->GetFID())
1458 : {
1459 2 : CPLError(CE_Failure, CPLE_AppDefined,
1460 : "Inconsistent values of FID and field of same name");
1461 2 : return OGRERR_FAILURE;
1462 : }
1463 : }
1464 :
1465 : /* -------------------------------------------------------------------- */
1466 : /* Form the UPDATE command. */
1467 : /* -------------------------------------------------------------------- */
1468 2067 : osCommand.Printf("UPDATE %s SET ", pszSqlTableName);
1469 :
1470 : /* Set the geometry */
1471 : const int nGeomStop = nUpdatedGeomFieldsCount >= 0
1472 2067 : ? nUpdatedGeomFieldsCount
1473 2051 : : poFeatureDefn->GetGeomFieldCount();
1474 4119 : for (int i = 0; i < nGeomStop; i++)
1475 : {
1476 2052 : const int iField =
1477 2052 : nUpdatedGeomFieldsCount >= 0 ? panUpdatedGeomFieldsIdx[i] : i;
1478 : OGRPGGeomFieldDefn *poGeomFieldDefn =
1479 2052 : poFeatureDefn->GetGeomFieldDefn(iField);
1480 2052 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iField);
1481 2052 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB)
1482 : {
1483 1017 : if (bNeedComma)
1484 0 : osCommand += ", ";
1485 : else
1486 1017 : bNeedComma = true;
1487 :
1488 1017 : osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
1489 1017 : osCommand += " = ";
1490 1017 : if (poGeom != nullptr)
1491 : {
1492 11 : if (!bWkbAsOid)
1493 : {
1494 : char *pszBytea =
1495 22 : GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
1496 11 : poDS->sPostGISVersion.nMinor);
1497 :
1498 11 : if (pszBytea != nullptr)
1499 : {
1500 11 : osCommand += "E'";
1501 11 : osCommand += pszBytea;
1502 11 : osCommand += '\'';
1503 11 : CPLFree(pszBytea);
1504 : }
1505 : else
1506 0 : osCommand += "NULL";
1507 : }
1508 : else
1509 : {
1510 0 : Oid oid = GeometryToOID(poGeom);
1511 :
1512 0 : if (oid != 0)
1513 : {
1514 0 : osCommand += CPLString().Printf("'%d' ", oid);
1515 : }
1516 : else
1517 0 : osCommand += "NULL";
1518 : }
1519 : }
1520 : else
1521 1006 : osCommand += "NULL";
1522 : }
1523 1035 : else if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
1524 1034 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
1525 : {
1526 1035 : if (bNeedComma)
1527 0 : osCommand += ", ";
1528 : else
1529 1035 : bNeedComma = true;
1530 :
1531 1035 : osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
1532 1035 : osCommand += " = ";
1533 1035 : if (poGeom != nullptr)
1534 : {
1535 29 : poGeom->closeRings();
1536 29 : poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
1537 29 : OGRGeometry::OGR_G_3D);
1538 29 : poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
1539 29 : OGRGeometry::OGR_G_MEASURED);
1540 : }
1541 :
1542 1035 : if (poGeom != nullptr)
1543 : {
1544 58 : char *pszHexEWKB = OGRGeometryToHexEWKB(
1545 : poGeom, poGeomFieldDefn->nSRSId,
1546 29 : poDS->sPostGISVersion.nMajor, poDS->sPostGISVersion.nMinor);
1547 29 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1548 : osCommand +=
1549 1 : CPLString().Printf("'%s'::GEOGRAPHY", pszHexEWKB);
1550 : else
1551 : osCommand +=
1552 28 : CPLString().Printf("'%s'::GEOMETRY", pszHexEWKB);
1553 29 : CPLFree(pszHexEWKB);
1554 : }
1555 : else
1556 1006 : osCommand += "NULL";
1557 : }
1558 : }
1559 :
1560 2067 : const int nStop = nUpdatedFieldsCount >= 0 ? nUpdatedFieldsCount
1561 2051 : : poFeatureDefn->GetFieldCount();
1562 4260 : for (int i = 0; i < nStop; i++)
1563 : {
1564 2193 : const int iField =
1565 2193 : nUpdatedFieldsCount >= 0 ? panUpdatedFieldsIdx[i] : i;
1566 2193 : if (iFIDAsRegularColumnIndex == iField)
1567 1 : continue;
1568 2192 : if (!poFeature->IsFieldSet(iField))
1569 0 : continue;
1570 2192 : if (poFeature->GetDefnRef()->GetFieldDefn(iField)->IsGenerated())
1571 2 : continue;
1572 :
1573 2190 : if (bNeedComma)
1574 2175 : osCommand += ", ";
1575 : else
1576 15 : bNeedComma = true;
1577 :
1578 4380 : osCommand += OGRPGEscapeColumnName(
1579 4380 : poFeatureDefn->GetFieldDefn(iField)->GetNameRef());
1580 2190 : osCommand += " = ";
1581 :
1582 2190 : if (poFeature->IsFieldNull(iField))
1583 : {
1584 64 : osCommand += "NULL";
1585 : }
1586 : else
1587 : {
1588 2126 : OGRPGCommonAppendFieldValue(osCommand, poFeature, iField,
1589 : OGRPGEscapeString, hPGConn);
1590 : }
1591 : }
1592 2067 : if (!bNeedComma) // nothing to do
1593 0 : return OGRERR_NONE;
1594 :
1595 : /* Add the WHERE clause */
1596 2067 : osCommand += " WHERE ";
1597 2067 : osCommand += OGRPGEscapeColumnName(pszFIDColumn);
1598 2067 : osCommand += +" = ";
1599 2067 : osCommand += CPLString().Printf(CPL_FRMT_GIB, poFeature->GetFID());
1600 :
1601 : /* -------------------------------------------------------------------- */
1602 : /* Execute the update. */
1603 : /* -------------------------------------------------------------------- */
1604 2067 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
1605 2067 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1606 : {
1607 0 : CPLError(CE_Failure, CPLE_AppDefined,
1608 : "UPDATE command for feature " CPL_FRMT_GIB
1609 : " failed.\n%s\nCommand: %s",
1610 : poFeature->GetFID(), PQerrorMessage(hPGConn),
1611 : osCommand.c_str());
1612 :
1613 0 : OGRPGClearResult(hResult);
1614 :
1615 0 : return OGRERR_FAILURE;
1616 : }
1617 :
1618 2067 : if (EQUAL(PQcmdStatus(hResult), "UPDATE 0"))
1619 9 : eErr = OGRERR_NON_EXISTING_FEATURE;
1620 : else
1621 2058 : eErr = OGRERR_NONE;
1622 :
1623 2067 : OGRPGClearResult(hResult);
1624 :
1625 2067 : return eErr;
1626 : }
1627 :
1628 : /************************************************************************/
1629 : /* ICreateFeature() */
1630 : /************************************************************************/
1631 :
1632 3866 : OGRErr OGRPGTableLayer::ICreateFeature(OGRFeature *poFeature)
1633 : {
1634 3866 : GetLayerDefn()->GetFieldCount();
1635 :
1636 3866 : if (!bUpdateAccess)
1637 : {
1638 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1639 : "CreateFeature");
1640 0 : return OGRERR_FAILURE;
1641 : }
1642 :
1643 3866 : if (nullptr == poFeature)
1644 : {
1645 0 : CPLError(CE_Failure, CPLE_AppDefined,
1646 : "NULL pointer to OGRFeature passed to CreateFeature().");
1647 0 : return OGRERR_FAILURE;
1648 : }
1649 :
1650 3866 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
1651 0 : return OGRERR_FAILURE;
1652 :
1653 : /* In case the FID column has also been created as a regular field */
1654 3866 : GIntBig nFID = poFeature->GetFID();
1655 3866 : if (iFIDAsRegularColumnIndex >= 0)
1656 : {
1657 4 : if (nFID == OGRNullFID)
1658 : {
1659 3 : if (poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex))
1660 : {
1661 2 : poFeature->SetFID(
1662 2 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex));
1663 : }
1664 : }
1665 : else
1666 : {
1667 2 : if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex) ||
1668 1 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
1669 : nFID)
1670 : {
1671 1 : CPLError(CE_Failure, CPLE_AppDefined,
1672 : "Inconsistent values of FID and field of same name");
1673 1 : return OGRERR_FAILURE;
1674 : }
1675 : }
1676 : }
1677 :
1678 : /* Auto-promote FID column to 64bit if necessary */
1679 3869 : if (pszFIDColumn != nullptr && !CPL_INT64_FITS_ON_INT32(nFID) &&
1680 4 : OGRLayer::GetMetadataItem(OLMD_FID64) == nullptr)
1681 : {
1682 2 : poDS->EndCopy();
1683 :
1684 2 : CPLString osCommand;
1685 : osCommand.Printf("ALTER TABLE %s ALTER COLUMN %s TYPE INT8",
1686 : pszSqlTableName,
1687 2 : OGRPGEscapeColumnName(pszFIDColumn).c_str());
1688 2 : PGconn *hPGConn = poDS->GetPGConn();
1689 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
1690 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1691 : {
1692 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
1693 : PQerrorMessage(hPGConn));
1694 :
1695 0 : OGRPGClearResult(hResult);
1696 :
1697 0 : return OGRERR_FAILURE;
1698 : }
1699 2 : OGRPGClearResult(hResult);
1700 :
1701 2 : OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
1702 : }
1703 :
1704 3865 : if (bFirstInsertion)
1705 : {
1706 195 : bFirstInsertion = FALSE;
1707 195 : if (CPLTestBool(CPLGetConfigOption("OGR_TRUNCATE", "NO")))
1708 : {
1709 1 : PGconn *hPGConn = poDS->GetPGConn();
1710 2 : CPLString osCommand;
1711 :
1712 1 : osCommand.Printf("TRUNCATE TABLE %s", pszSqlTableName);
1713 1 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1714 1 : OGRPGClearResult(hResult);
1715 : }
1716 : }
1717 :
1718 : // We avoid testing the config option too often.
1719 3865 : if (bUseCopy == USE_COPY_UNSET)
1720 53 : bUseCopy = CPLTestBool(CPLGetConfigOption("PG_USE_COPY", "NO"));
1721 :
1722 : OGRErr eErr;
1723 3865 : if (!bUseCopy)
1724 : {
1725 120 : eErr = CreateFeatureViaInsert(poFeature);
1726 : }
1727 : else
1728 : {
1729 : /* If there's a unset field with a default value, then we must use */
1730 : /* a specific INSERT statement to avoid unset fields to be bound to NULL
1731 : */
1732 3745 : bool bHasDefaultValue = false;
1733 3745 : const int nFieldCount = poFeatureDefn->GetFieldCount();
1734 8972 : for (int iField = 0; iField < nFieldCount; iField++)
1735 : {
1736 6487 : if (!poFeature->IsFieldSet(iField) &&
1737 1259 : poFeature->GetFieldDefnRef(iField)->GetDefault() != nullptr)
1738 : {
1739 1 : bHasDefaultValue = true;
1740 1 : break;
1741 : }
1742 : }
1743 3745 : if (bHasDefaultValue)
1744 : {
1745 1 : eErr = CreateFeatureViaInsert(poFeature);
1746 : }
1747 : else
1748 : {
1749 : bool bFIDSet =
1750 3744 : (pszFIDColumn != nullptr && poFeature->GetFID() != OGRNullFID);
1751 3744 : if (bCopyActive && bFIDSet != bFIDColumnInCopyFields)
1752 : {
1753 1 : eErr = CreateFeatureViaInsert(poFeature);
1754 : }
1755 189 : else if (!bCopyActive && poFeatureDefn->GetFieldCount() == 0 &&
1756 3932 : poFeatureDefn->GetGeomFieldCount() == 0 && !bFIDSet)
1757 : {
1758 4 : eErr = CreateFeatureViaInsert(poFeature);
1759 : }
1760 : else
1761 : {
1762 3739 : if (!bCopyActive)
1763 : {
1764 : /* This is a heuristics. If the first feature to be copied
1765 : * has a */
1766 : /* FID set (and that a FID column has been identified), then
1767 : * we will */
1768 : /* try to copy FID values from features. Otherwise, we will
1769 : * not */
1770 : /* do and assume that the FID column is an autoincremented
1771 : * column. */
1772 185 : bFIDColumnInCopyFields = bFIDSet;
1773 185 : bNeedToUpdateSequence = bFIDSet;
1774 : }
1775 :
1776 3739 : eErr = CreateFeatureViaCopy(poFeature);
1777 3739 : if (bFIDSet)
1778 14 : bAutoFIDOnCreateViaCopy = FALSE;
1779 3739 : if (eErr == OGRERR_NONE && bAutoFIDOnCreateViaCopy)
1780 : {
1781 3704 : poFeature->SetFID(++iNextShapeId);
1782 : }
1783 : }
1784 : }
1785 : }
1786 :
1787 3865 : if (eErr == OGRERR_NONE && iFIDAsRegularColumnIndex >= 0)
1788 : {
1789 3 : poFeature->SetField(iFIDAsRegularColumnIndex, poFeature->GetFID());
1790 : }
1791 :
1792 3865 : return eErr;
1793 : }
1794 :
1795 : /************************************************************************/
1796 : /* OGRPGEscapeColumnName( ) */
1797 : /************************************************************************/
1798 :
1799 16778 : CPLString OGRPGEscapeColumnName(const char *pszColumnName)
1800 : {
1801 16778 : CPLString osStr = "\"";
1802 :
1803 16778 : char ch = '\0';
1804 209823 : for (int i = 0; (ch = pszColumnName[i]) != '\0'; i++)
1805 : {
1806 193045 : if (ch == '"')
1807 0 : osStr.append(1, ch);
1808 193045 : osStr.append(1, ch);
1809 : }
1810 :
1811 16778 : osStr += "\"";
1812 :
1813 16778 : return osStr;
1814 : }
1815 :
1816 : /************************************************************************/
1817 : /* OGRPGEscapeString( ) */
1818 : /************************************************************************/
1819 :
1820 3647 : CPLString OGRPGEscapeString(void *hPGConnIn, const char *pszStrValue,
1821 : int nMaxLength, const char *pszTableName,
1822 : const char *pszFieldName)
1823 : {
1824 3647 : PGconn *hPGConn = reinterpret_cast<PGconn *>(hPGConnIn);
1825 3647 : CPLString osCommand;
1826 :
1827 : /* We need to quote and escape string fields. */
1828 3647 : osCommand += "'";
1829 :
1830 3647 : int nSrcLen = static_cast<int>(strlen(pszStrValue));
1831 3647 : int nSrcLenUTF = CPLStrlenUTF8(pszStrValue);
1832 :
1833 3647 : if (nMaxLength > 0 && nSrcLenUTF > nMaxLength)
1834 : {
1835 0 : CPLDebug("PG", "Truncated %s.%s field value '%s' to %d characters.",
1836 : pszTableName, pszFieldName, pszStrValue, nMaxLength);
1837 :
1838 0 : int iUTF8Char = 0;
1839 0 : for (int iChar = 0; iChar < nSrcLen; iChar++)
1840 : {
1841 0 : if (((reinterpret_cast<const unsigned char *>(pszStrValue))[iChar] &
1842 : 0xc0) != 0x80)
1843 : {
1844 0 : if (iUTF8Char == nMaxLength)
1845 : {
1846 0 : nSrcLen = iChar;
1847 0 : break;
1848 : }
1849 0 : iUTF8Char++;
1850 : }
1851 : }
1852 : }
1853 :
1854 3647 : char *pszDestStr = static_cast<char *>(CPLMalloc(2 * nSrcLen + 1));
1855 :
1856 3647 : int nError = 0;
1857 3647 : PQescapeStringConn(hPGConn, pszDestStr, pszStrValue, nSrcLen, &nError);
1858 3647 : if (nError == 0)
1859 3647 : osCommand += pszDestStr;
1860 : else
1861 0 : CPLError(CE_Warning, CPLE_AppDefined,
1862 : "PQescapeString(): %s\n"
1863 : " input: '%s'\n"
1864 : " got: '%s'\n",
1865 : PQerrorMessage(hPGConn), pszStrValue, pszDestStr);
1866 :
1867 3647 : CPLFree(pszDestStr);
1868 :
1869 3647 : osCommand += "'";
1870 :
1871 7294 : return osCommand;
1872 : }
1873 :
1874 : /************************************************************************/
1875 : /* CreateFeatureViaInsert() */
1876 : /************************************************************************/
1877 :
1878 126 : OGRErr OGRPGTableLayer::CreateFeatureViaInsert(OGRFeature *poFeature)
1879 :
1880 : {
1881 126 : PGconn *hPGConn = poDS->GetPGConn();
1882 252 : CPLString osCommand;
1883 126 : int bNeedComma = FALSE;
1884 :
1885 126 : int bEmptyInsert = FALSE;
1886 :
1887 126 : poDS->EndCopy();
1888 :
1889 : /* -------------------------------------------------------------------- */
1890 : /* Form the INSERT command. */
1891 : /* -------------------------------------------------------------------- */
1892 126 : osCommand.Printf("INSERT INTO %s (", pszSqlTableName);
1893 :
1894 237 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
1895 : {
1896 111 : OGRGeomFieldDefn *poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(i);
1897 111 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
1898 111 : if (poGeom == nullptr)
1899 28 : continue;
1900 83 : if (!bNeedComma)
1901 83 : bNeedComma = TRUE;
1902 : else
1903 0 : osCommand += ", ";
1904 83 : osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()) + " ";
1905 : }
1906 :
1907 : /* Use case of ogr_pg_60 test */
1908 126 : if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
1909 : {
1910 7 : bNeedToUpdateSequence = true;
1911 :
1912 7 : if (bNeedComma)
1913 5 : osCommand += ", ";
1914 :
1915 7 : osCommand = osCommand + OGRPGEscapeColumnName(pszFIDColumn) + " ";
1916 7 : bNeedComma = TRUE;
1917 : }
1918 : else
1919 : {
1920 119 : UpdateSequenceIfNeeded();
1921 : }
1922 :
1923 126 : const int nFieldCount = poFeatureDefn->GetFieldCount();
1924 572 : for (int i = 0; i < nFieldCount; i++)
1925 : {
1926 446 : if (iFIDAsRegularColumnIndex == i)
1927 1 : continue;
1928 445 : if (!poFeature->IsFieldSet(i))
1929 185 : continue;
1930 260 : if (poFeature->GetDefnRef()->GetFieldDefn(i)->IsGenerated())
1931 2 : continue;
1932 :
1933 258 : if (!bNeedComma)
1934 28 : bNeedComma = TRUE;
1935 : else
1936 230 : osCommand += ", ";
1937 :
1938 : osCommand +=
1939 258 : OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1940 : }
1941 :
1942 126 : if (!bNeedComma)
1943 13 : bEmptyInsert = TRUE;
1944 :
1945 126 : osCommand += ") VALUES (";
1946 :
1947 : /* Set the geometry */
1948 126 : bNeedComma = FALSE;
1949 237 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
1950 : {
1951 : const OGRPGGeomFieldDefn *poGeomFieldDefn =
1952 111 : poFeatureDefn->GetGeomFieldDefn(i);
1953 111 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
1954 111 : if (poGeom == nullptr)
1955 28 : continue;
1956 83 : if (bNeedComma)
1957 0 : osCommand += ", ";
1958 : else
1959 83 : bNeedComma = TRUE;
1960 :
1961 83 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
1962 83 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
1963 : {
1964 54 : CheckGeomTypeCompatibility(i, poGeom);
1965 :
1966 54 : poGeom->closeRings();
1967 54 : poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
1968 54 : OGRGeometry::OGR_G_3D);
1969 54 : poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
1970 54 : OGRGeometry::OGR_G_MEASURED);
1971 :
1972 54 : int nSRSId = poGeomFieldDefn->nSRSId;
1973 :
1974 108 : char *pszHexEWKB = OGRGeometryToHexEWKB(
1975 54 : poGeom, nSRSId, poDS->sPostGISVersion.nMajor,
1976 54 : poDS->sPostGISVersion.nMinor);
1977 54 : if (!pszHexEWKB || pszHexEWKB[0] == 0)
1978 : {
1979 0 : CPLFree(pszHexEWKB);
1980 0 : return OGRERR_FAILURE;
1981 : }
1982 54 : osCommand += '\'';
1983 : try
1984 : {
1985 54 : osCommand += pszHexEWKB;
1986 : }
1987 0 : catch (const std::bad_alloc &)
1988 : {
1989 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1990 : "Out of memory: too large geometry");
1991 0 : CPLFree(pszHexEWKB);
1992 0 : return OGRERR_FAILURE;
1993 : }
1994 54 : osCommand += "'::";
1995 54 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1996 0 : osCommand += "GEOGRAPHY";
1997 : else
1998 54 : osCommand += "GEOMETRY";
1999 54 : CPLFree(pszHexEWKB);
2000 : }
2001 29 : else if (!bWkbAsOid)
2002 : {
2003 : char *pszBytea =
2004 58 : GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
2005 29 : poDS->sPostGISVersion.nMinor);
2006 :
2007 29 : if (!pszBytea)
2008 : {
2009 0 : return OGRERR_FAILURE;
2010 : }
2011 29 : osCommand += "E'";
2012 : try
2013 : {
2014 29 : osCommand += pszBytea;
2015 : }
2016 0 : catch (const std::bad_alloc &)
2017 : {
2018 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2019 : "Out of memory: too large geometry");
2020 0 : CPLFree(pszBytea);
2021 0 : return OGRERR_FAILURE;
2022 : }
2023 29 : osCommand += '\'';
2024 29 : CPLFree(pszBytea);
2025 : }
2026 0 : else if (poGeomFieldDefn->ePostgisType ==
2027 : GEOM_TYPE_WKB /* && bWkbAsOid */)
2028 : {
2029 0 : Oid oid = GeometryToOID(poGeom);
2030 :
2031 0 : if (oid != 0)
2032 : {
2033 0 : osCommand += CPLString().Printf("'%d' ", oid);
2034 : }
2035 : else
2036 0 : osCommand += "''";
2037 : }
2038 : }
2039 :
2040 126 : if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
2041 : {
2042 7 : if (bNeedComma)
2043 5 : osCommand += ", ";
2044 7 : osCommand += CPLString().Printf(CPL_FRMT_GIB " ", poFeature->GetFID());
2045 7 : bNeedComma = TRUE;
2046 : }
2047 :
2048 572 : for (int i = 0; i < nFieldCount; i++)
2049 : {
2050 446 : if (iFIDAsRegularColumnIndex == i)
2051 1 : continue;
2052 445 : if (!poFeature->IsFieldSet(i))
2053 185 : continue;
2054 260 : if (poFeature->GetDefnRef()->GetFieldDefn(i)->IsGenerated())
2055 2 : continue;
2056 :
2057 258 : if (bNeedComma)
2058 230 : osCommand += ", ";
2059 : else
2060 28 : bNeedComma = TRUE;
2061 :
2062 258 : OGRPGCommonAppendFieldValue(osCommand, poFeature, i, OGRPGEscapeString,
2063 : hPGConn);
2064 : }
2065 :
2066 126 : osCommand += ")";
2067 :
2068 126 : if (bEmptyInsert)
2069 13 : osCommand.Printf("INSERT INTO %s DEFAULT VALUES", pszSqlTableName);
2070 :
2071 126 : int bReturnRequested = FALSE;
2072 : /* We only get the FID, but we also could add the unset fields to get */
2073 : /* the default values */
2074 245 : if (bRetrieveFID && pszFIDColumn != nullptr &&
2075 119 : poFeature->GetFID() == OGRNullFID)
2076 : {
2077 112 : if (bSkipConflicts)
2078 : {
2079 1 : CPLError(CE_Failure, CPLE_AppDefined,
2080 : "fid retrieval and skipping conflicts are not supported "
2081 : "at the same time.");
2082 1 : return OGRERR_FAILURE;
2083 : }
2084 111 : bReturnRequested = TRUE;
2085 111 : osCommand += " RETURNING ";
2086 111 : osCommand += OGRPGEscapeColumnName(pszFIDColumn);
2087 : }
2088 14 : else if (bSkipConflicts)
2089 2 : osCommand += " ON CONFLICT DO NOTHING";
2090 :
2091 : /* -------------------------------------------------------------------- */
2092 : /* Execute the insert. */
2093 : /* -------------------------------------------------------------------- */
2094 125 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2095 111 : if (bReturnRequested && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
2096 236 : PQntuples(hResult) == 1 && PQnfields(hResult) == 1)
2097 : {
2098 109 : const char *pszFID = PQgetvalue(hResult, 0, 0);
2099 109 : poFeature->SetFID(CPLAtoGIntBig(pszFID));
2100 : }
2101 16 : else if (bReturnRequested || PQresultStatus(hResult) != PGRES_COMMAND_OK)
2102 : {
2103 3 : CPLError(CE_Failure, CPLE_AppDefined,
2104 : "INSERT command for new feature failed.\n%s\nCommand: %s",
2105 6 : PQerrorMessage(hPGConn), osCommand.substr(0, 1024).c_str());
2106 :
2107 3 : if (!bHasWarnedAlreadySetFID && poFeature->GetFID() != OGRNullFID &&
2108 0 : pszFIDColumn != nullptr)
2109 : {
2110 0 : bHasWarnedAlreadySetFID = TRUE;
2111 0 : CPLError(CE_Warning, CPLE_AppDefined,
2112 : "You've inserted feature with an already set FID and "
2113 : "that's perhaps the reason for the failure. "
2114 : "If so, this can happen if you reuse the same feature "
2115 : "object for sequential insertions. "
2116 : "Indeed, since GDAL 1.8.0, the FID of an inserted feature "
2117 : "is got from the server, so it is not a good idea"
2118 : "to reuse it afterwards... All in all, try unsetting the "
2119 : "FID with SetFID(-1) before calling CreateFeature()");
2120 : }
2121 :
2122 3 : OGRPGClearResult(hResult);
2123 :
2124 3 : return OGRERR_FAILURE;
2125 : }
2126 :
2127 122 : OGRPGClearResult(hResult);
2128 :
2129 122 : return OGRERR_NONE;
2130 : }
2131 :
2132 : /************************************************************************/
2133 : /* CreateFeatureViaCopy() */
2134 : /************************************************************************/
2135 :
2136 3739 : OGRErr OGRPGTableLayer::CreateFeatureViaCopy(OGRFeature *poFeature)
2137 : {
2138 3739 : PGconn *hPGConn = poDS->GetPGConn();
2139 7478 : CPLString osCommand;
2140 :
2141 : /* Tell the datasource we are now planning to copy data */
2142 3739 : poDS->StartCopy(this);
2143 :
2144 : /* First process geometry */
2145 7454 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
2146 : {
2147 : const OGRPGGeomFieldDefn *poGeomFieldDefn =
2148 3715 : poFeatureDefn->GetGeomFieldDefn(i);
2149 3715 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
2150 :
2151 3715 : char *pszGeom = nullptr;
2152 3715 : if (nullptr != poGeom)
2153 : {
2154 1648 : CheckGeomTypeCompatibility(i, poGeom);
2155 :
2156 1648 : poGeom->closeRings();
2157 1648 : poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
2158 1648 : OGRGeometry::OGR_G_3D);
2159 1648 : poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
2160 1648 : OGRGeometry::OGR_G_MEASURED);
2161 :
2162 1648 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB)
2163 789 : pszGeom = GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
2164 789 : poDS->sPostGISVersion.nMinor);
2165 : else
2166 859 : pszGeom = OGRGeometryToHexEWKB(poGeom, poGeomFieldDefn->nSRSId,
2167 859 : poDS->sPostGISVersion.nMajor,
2168 859 : poDS->sPostGISVersion.nMinor);
2169 1648 : if (!pszGeom || pszGeom[0] == 0)
2170 : {
2171 0 : CPLFree(pszGeom);
2172 0 : return OGRERR_FAILURE;
2173 : }
2174 : }
2175 :
2176 3715 : if (!osCommand.empty())
2177 12 : osCommand += "\t";
2178 :
2179 3715 : if (pszGeom)
2180 : {
2181 : try
2182 : {
2183 1648 : osCommand += pszGeom;
2184 : }
2185 0 : catch (const std::bad_alloc &)
2186 : {
2187 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2188 : "Out of memory: too large geometry");
2189 0 : CPLFree(pszGeom);
2190 0 : return OGRERR_FAILURE;
2191 : }
2192 1648 : CPLFree(pszGeom);
2193 : }
2194 : else
2195 : {
2196 2067 : osCommand += "\\N";
2197 : }
2198 : }
2199 :
2200 7478 : std::vector<bool> abFieldsToInclude(poFeature->GetFieldCount(), true);
2201 8963 : for (size_t i = 0; i < abFieldsToInclude.size(); i++)
2202 10448 : abFieldsToInclude[i] = !poFeature->GetDefnRef()
2203 5224 : ->GetFieldDefn(static_cast<int>(i))
2204 5224 : ->IsGenerated();
2205 :
2206 3739 : if (bFIDColumnInCopyFields)
2207 : {
2208 14 : OGRPGCommonAppendCopyFID(osCommand, poFeature);
2209 : }
2210 3739 : OGRPGCommonAppendCopyRegularFields(osCommand, poFeature, pszFIDColumn,
2211 : abFieldsToInclude, OGRPGEscapeString,
2212 : hPGConn);
2213 :
2214 : /* Add end of line marker */
2215 3739 : osCommand += "\n";
2216 :
2217 : // PostgreSQL doesn't provide very helpful reporting of invalid UTF-8
2218 : // content in COPY mode.
2219 7478 : if (poDS->IsUTF8ClientEncoding() &&
2220 3739 : !CPLIsUTF8(osCommand.c_str(), static_cast<int>(osCommand.size())))
2221 : {
2222 0 : CPLError(CE_Failure, CPLE_AppDefined,
2223 : "Non UTF-8 content found when writing feature " CPL_FRMT_GIB
2224 : " of layer %s: %s",
2225 0 : poFeature->GetFID(), poFeatureDefn->GetName(),
2226 : osCommand.c_str());
2227 0 : return OGRERR_FAILURE;
2228 : }
2229 :
2230 : /* ------------------------------------------------------------ */
2231 : /* Execute the copy. */
2232 : /* ------------------------------------------------------------ */
2233 :
2234 3739 : OGRErr result = OGRERR_NONE;
2235 :
2236 3739 : int copyResult = PQputCopyData(hPGConn, osCommand.c_str(),
2237 3739 : static_cast<int>(osCommand.size()));
2238 : #ifdef DEBUG_VERBOSE
2239 : CPLDebug("PG", "PQputCopyData(%s)", osCommand.c_str());
2240 : #endif
2241 :
2242 3739 : switch (copyResult)
2243 : {
2244 0 : case 0:
2245 0 : CPLError(CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
2246 0 : result = OGRERR_FAILURE;
2247 0 : break;
2248 0 : case -1:
2249 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
2250 : PQerrorMessage(hPGConn));
2251 0 : result = OGRERR_FAILURE;
2252 0 : break;
2253 : }
2254 :
2255 3739 : return result;
2256 : }
2257 :
2258 : /************************************************************************/
2259 : /* TestCapability() */
2260 : /************************************************************************/
2261 :
2262 807 : int OGRPGTableLayer::TestCapability(const char *pszCap)
2263 :
2264 : {
2265 807 : if (bUpdateAccess)
2266 : {
2267 802 : if (EQUAL(pszCap, OLCSequentialWrite) ||
2268 795 : EQUAL(pszCap, OLCCreateField) ||
2269 783 : EQUAL(pszCap, OLCCreateGeomField) ||
2270 782 : EQUAL(pszCap, OLCDeleteField) || EQUAL(pszCap, OLCAlterFieldDefn) ||
2271 758 : EQUAL(pszCap, OLCAlterGeomFieldDefn) || EQUAL(pszCap, OLCRename))
2272 53 : return TRUE;
2273 :
2274 749 : else if (EQUAL(pszCap, OLCRandomWrite) ||
2275 742 : EQUAL(pszCap, OLCUpdateFeature) ||
2276 740 : EQUAL(pszCap, OLCDeleteFeature))
2277 : {
2278 19 : GetLayerDefn()->GetFieldCount();
2279 19 : return pszFIDColumn != nullptr;
2280 : }
2281 : }
2282 :
2283 735 : if (EQUAL(pszCap, OLCRandomRead))
2284 : {
2285 12 : GetLayerDefn()->GetFieldCount();
2286 12 : return pszFIDColumn != nullptr;
2287 : }
2288 :
2289 723 : else if (EQUAL(pszCap, OLCFastFeatureCount) ||
2290 569 : EQUAL(pszCap, OLCFastSetNextByIndex))
2291 : {
2292 185 : if (m_poFilterGeom == nullptr)
2293 159 : return TRUE;
2294 26 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
2295 26 : if (poFeatureDefn->GetGeomFieldCount() > 0)
2296 : poGeomFieldDefn =
2297 26 : poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
2298 52 : return poGeomFieldDefn == nullptr ||
2299 26 : (poDS->sPostGISVersion.nMajor >= 0 &&
2300 23 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
2301 34 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY));
2302 : }
2303 :
2304 538 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
2305 : {
2306 2 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
2307 2 : if (poFeatureDefn->GetGeomFieldCount() > 0)
2308 : poGeomFieldDefn =
2309 2 : poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
2310 4 : return poGeomFieldDefn == nullptr ||
2311 2 : (poDS->sPostGISVersion.nMajor >= 0 &&
2312 1 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
2313 2 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY));
2314 : }
2315 :
2316 536 : else if (EQUAL(pszCap, OLCTransactions))
2317 7 : return TRUE;
2318 :
2319 529 : else if (EQUAL(pszCap, OLCFastGetExtent) ||
2320 494 : EQUAL(pszCap, OLCFastGetExtent3D))
2321 : {
2322 37 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
2323 37 : if (poFeatureDefn->GetGeomFieldCount() > 0)
2324 37 : poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
2325 37 : return poGeomFieldDefn != nullptr &&
2326 70 : poDS->sPostGISVersion.nMajor >= 0 &&
2327 70 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY;
2328 : }
2329 :
2330 492 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
2331 7 : return TRUE;
2332 :
2333 485 : else if (EQUAL(pszCap, OLCCurveGeometries))
2334 239 : return TRUE;
2335 :
2336 246 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
2337 213 : return TRUE;
2338 :
2339 33 : else if (EQUAL(pszCap, OLCZGeometries))
2340 15 : return TRUE;
2341 :
2342 : else
2343 18 : return FALSE;
2344 : }
2345 :
2346 : /************************************************************************/
2347 : /* CreateField() */
2348 : /************************************************************************/
2349 :
2350 607 : OGRErr OGRPGTableLayer::CreateField(const OGRFieldDefn *poFieldIn,
2351 : int bApproxOK)
2352 :
2353 : {
2354 607 : PGconn *hPGConn = poDS->GetPGConn();
2355 1214 : CPLString osCommand;
2356 1214 : CPLString osFieldType;
2357 1214 : OGRFieldDefn oField(poFieldIn);
2358 :
2359 607 : GetLayerDefn()->GetFieldCount();
2360 :
2361 607 : if (!bUpdateAccess)
2362 : {
2363 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2364 : "CreateField");
2365 0 : return OGRERR_FAILURE;
2366 : }
2367 :
2368 609 : if (pszFIDColumn != nullptr && EQUAL(oField.GetNameRef(), pszFIDColumn) &&
2369 1216 : oField.GetType() != OFTInteger && oField.GetType() != OFTInteger64)
2370 : {
2371 1 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
2372 : oField.GetNameRef());
2373 1 : return OGRERR_FAILURE;
2374 : }
2375 :
2376 : /* -------------------------------------------------------------------- */
2377 : /* Do we want to "launder" the column names into Postgres */
2378 : /* friendly format? */
2379 : /* -------------------------------------------------------------------- */
2380 606 : if (bLaunderColumnNames)
2381 : {
2382 : char *pszSafeName =
2383 604 : OGRPGCommonLaunderName(oField.GetNameRef(), "PG", m_bUTF8ToASCII);
2384 :
2385 604 : oField.SetName(pszSafeName);
2386 604 : CPLFree(pszSafeName);
2387 :
2388 604 : if (EQUAL(oField.GetNameRef(), "oid"))
2389 : {
2390 0 : CPLError(CE_Warning, CPLE_AppDefined,
2391 : "Renaming field 'oid' to 'oid_' to avoid conflict with "
2392 : "internal oid field.");
2393 0 : oField.SetName("oid_");
2394 : }
2395 : }
2396 :
2397 : const char *pszOverrideType =
2398 606 : CSLFetchNameValue(papszOverrideColumnTypes, oField.GetNameRef());
2399 606 : if (pszOverrideType != nullptr)
2400 3 : osFieldType = pszOverrideType;
2401 : else
2402 : {
2403 1206 : osFieldType = OGRPGCommonLayerGetType(
2404 1206 : oField, CPL_TO_BOOL(bPreservePrecision), CPL_TO_BOOL(bApproxOK));
2405 603 : if (osFieldType.empty())
2406 0 : return OGRERR_FAILURE;
2407 : }
2408 :
2409 1212 : CPLString osConstraints;
2410 606 : if (!oField.IsNullable())
2411 1 : osConstraints += " NOT NULL";
2412 606 : if (oField.IsUnique())
2413 4 : osConstraints += " UNIQUE";
2414 606 : if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
2415 : {
2416 8 : osConstraints += " DEFAULT ";
2417 8 : osConstraints += OGRPGCommonLayerGetPGDefault(&oField);
2418 : }
2419 :
2420 1212 : std::string osCommentON;
2421 606 : if (!oField.GetComment().empty())
2422 : {
2423 4 : osCommentON = "COMMENT ON COLUMN ";
2424 4 : osCommentON += pszSqlTableName;
2425 4 : osCommentON += '.';
2426 4 : osCommentON += OGRPGEscapeColumnName(oField.GetNameRef());
2427 4 : osCommentON += " IS ";
2428 4 : osCommentON += OGRPGEscapeString(hPGConn, oField.GetComment().c_str());
2429 : }
2430 :
2431 : /* -------------------------------------------------------------------- */
2432 : /* Create the new field. */
2433 : /* -------------------------------------------------------------------- */
2434 606 : if (bDeferredCreation)
2435 : {
2436 582 : if (!(pszFIDColumn != nullptr &&
2437 291 : EQUAL(pszFIDColumn, oField.GetNameRef())))
2438 : {
2439 290 : osCreateTable += ", ";
2440 290 : osCreateTable += OGRPGEscapeColumnName(oField.GetNameRef());
2441 290 : osCreateTable += " ";
2442 290 : osCreateTable += osFieldType;
2443 290 : osCreateTable += osConstraints;
2444 :
2445 290 : if (!osCommentON.empty())
2446 1 : m_aosDeferredCommentOnColumns.push_back(osCommentON);
2447 : }
2448 : }
2449 : else
2450 : {
2451 315 : poDS->EndCopy();
2452 :
2453 : osCommand.Printf("ALTER TABLE %s ADD COLUMN %s %s", pszSqlTableName,
2454 630 : OGRPGEscapeColumnName(oField.GetNameRef()).c_str(),
2455 630 : osFieldType.c_str());
2456 315 : osCommand += osConstraints;
2457 :
2458 315 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2459 315 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2460 : {
2461 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2462 : PQerrorMessage(hPGConn));
2463 :
2464 0 : OGRPGClearResult(hResult);
2465 :
2466 0 : return OGRERR_FAILURE;
2467 : }
2468 :
2469 315 : OGRPGClearResult(hResult);
2470 :
2471 315 : if (!osCommentON.empty())
2472 : {
2473 3 : hResult = OGRPG_PQexec(hPGConn, osCommentON.c_str());
2474 3 : OGRPGClearResult(hResult);
2475 : }
2476 : }
2477 :
2478 606 : whileUnsealing(poFeatureDefn)->AddFieldDefn(&oField);
2479 :
2480 606 : if (pszFIDColumn != nullptr && EQUAL(oField.GetNameRef(), pszFIDColumn))
2481 : {
2482 1 : iFIDAsRegularColumnIndex = poFeatureDefn->GetFieldCount() - 1;
2483 : }
2484 :
2485 606 : return OGRERR_NONE;
2486 : }
2487 :
2488 : /************************************************************************/
2489 : /* RunAddGeometryColumn() */
2490 : /************************************************************************/
2491 :
2492 : OGRErr
2493 8 : OGRPGTableLayer::RunAddGeometryColumn(const OGRPGGeomFieldDefn *poGeomField)
2494 : {
2495 8 : PGconn *hPGConn = poDS->GetPGConn();
2496 :
2497 8 : const char *pszGeometryType = OGRToOGCGeomType(poGeomField->GetType());
2498 8 : const char *suffix = "";
2499 8 : int dim = 2;
2500 8 : if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
2501 4 : (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
2502 2 : dim = 4;
2503 6 : else if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
2504 : {
2505 1 : if (!(wkbFlatten(poGeomField->GetType()) == wkbUnknown))
2506 1 : suffix = "M";
2507 1 : dim = 3;
2508 : }
2509 5 : else if (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D)
2510 2 : dim = 3;
2511 :
2512 16 : CPLString osCommand;
2513 : osCommand.Printf(
2514 : "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
2515 16 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
2516 16 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
2517 16 : OGRPGEscapeString(hPGConn, poGeomField->GetNameRef()).c_str(),
2518 32 : poGeomField->nSRSId, pszGeometryType, suffix, dim);
2519 :
2520 8 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2521 :
2522 8 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
2523 : {
2524 0 : CPLError(CE_Failure, CPLE_AppDefined,
2525 0 : "AddGeometryColumn failed for layer %s.", GetName());
2526 :
2527 0 : OGRPGClearResult(hResult);
2528 :
2529 0 : return OGRERR_FAILURE;
2530 : }
2531 :
2532 8 : OGRPGClearResult(hResult);
2533 :
2534 8 : if (!poGeomField->IsNullable())
2535 : {
2536 : osCommand.Printf(
2537 : "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
2538 0 : OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str());
2539 :
2540 0 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2541 0 : OGRPGClearResult(hResult);
2542 : }
2543 :
2544 8 : return OGRERR_NONE;
2545 : }
2546 :
2547 : /************************************************************************/
2548 : /* RunCreateSpatialIndex() */
2549 : /************************************************************************/
2550 :
2551 : OGRErr
2552 129 : OGRPGTableLayer::RunCreateSpatialIndex(const OGRPGGeomFieldDefn *poGeomField,
2553 : int nIdx)
2554 : {
2555 129 : PGconn *hPGConn = poDS->GetPGConn();
2556 258 : CPLString osCommand;
2557 :
2558 : const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
2559 258 : pszTableName, poGeomField->GetNameRef(), nIdx));
2560 :
2561 : osCommand.Printf("CREATE INDEX %s ON %s USING %s (%s)",
2562 258 : OGRPGEscapeColumnName(osIndexName.c_str()).c_str(),
2563 : pszSqlTableName, osSpatialIndexType.c_str(),
2564 387 : OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str());
2565 :
2566 129 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2567 :
2568 129 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
2569 : {
2570 0 : CPLError(CE_Failure, CPLE_AppDefined,
2571 0 : "CREATE INDEX failed for layer %s.", GetName());
2572 :
2573 0 : OGRPGClearResult(hResult);
2574 :
2575 0 : return OGRERR_FAILURE;
2576 : }
2577 :
2578 129 : OGRPGClearResult(hResult);
2579 :
2580 129 : return OGRERR_NONE;
2581 : }
2582 :
2583 : /************************************************************************/
2584 : /* CreateGeomField() */
2585 : /************************************************************************/
2586 :
2587 18 : OGRErr OGRPGTableLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
2588 : CPL_UNUSED int bApproxOK)
2589 : {
2590 18 : OGRwkbGeometryType eType = poGeomFieldIn->GetType();
2591 18 : if (eType == wkbNone)
2592 : {
2593 0 : CPLError(CE_Failure, CPLE_AppDefined,
2594 : "Cannot create geometry field of type wkbNone");
2595 0 : return OGRERR_FAILURE;
2596 : }
2597 :
2598 : // Check if GEOMETRY_NAME layer creation option was set, but no initial
2599 : // column was created in ICreateLayer()
2600 18 : CPLString osGeomFieldName = (m_osFirstGeometryFieldName.size())
2601 2 : ? m_osFirstGeometryFieldName
2602 36 : : CPLString(poGeomFieldIn->GetNameRef());
2603 18 : m_osFirstGeometryFieldName = ""; // reset for potential next geom columns
2604 :
2605 : auto poGeomField =
2606 36 : std::make_unique<OGRPGGeomFieldDefn>(this, osGeomFieldName);
2607 18 : if (EQUAL(poGeomField->GetNameRef(), ""))
2608 : {
2609 0 : if (poFeatureDefn->GetGeomFieldCount() == 0)
2610 0 : poGeomField->SetName(EQUAL(m_osLCOGeomType.c_str(), "geography")
2611 : ? "the_geog"
2612 : : "wkb_geometry");
2613 : else
2614 0 : poGeomField->SetName(CPLSPrintf(
2615 0 : "wkb_geometry%d", poFeatureDefn->GetGeomFieldCount() + 1));
2616 : }
2617 18 : const auto poSRSIn = poGeomFieldIn->GetSpatialRef();
2618 18 : if (poSRSIn)
2619 : {
2620 4 : auto l_poSRS = poSRSIn->Clone();
2621 4 : l_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2622 4 : poGeomField->SetSpatialRef(l_poSRS);
2623 4 : l_poSRS->Release();
2624 : }
2625 : /* -------------------------------------------------------------------- */
2626 : /* Do we want to "launder" the column names into Postgres */
2627 : /* friendly format? */
2628 : /* -------------------------------------------------------------------- */
2629 18 : if (bLaunderColumnNames)
2630 : {
2631 18 : char *pszSafeName = OGRPGCommonLaunderName(poGeomField->GetNameRef(),
2632 18 : "PG", m_bUTF8ToASCII);
2633 :
2634 18 : poGeomField->SetName(pszSafeName);
2635 18 : CPLFree(pszSafeName);
2636 : }
2637 :
2638 18 : const OGRSpatialReference *poSRS = poGeomField->GetSpatialRef();
2639 18 : int nSRSId = poDS->GetUndefinedSRID();
2640 18 : if (nForcedSRSId != UNDETERMINED_SRID)
2641 0 : nSRSId = nForcedSRSId;
2642 18 : else if (poSRS != nullptr)
2643 4 : nSRSId = poDS->FetchSRSId(poSRS);
2644 :
2645 18 : int GeometryTypeFlags = 0;
2646 18 : if (OGR_GT_HasZ(eType))
2647 4 : GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
2648 18 : if (OGR_GT_HasM(eType))
2649 2 : GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
2650 18 : if (nForcedGeometryTypeFlags >= 0)
2651 : {
2652 2 : GeometryTypeFlags = nForcedGeometryTypeFlags;
2653 : eType =
2654 2 : OGR_GT_SetModifier(eType, GeometryTypeFlags & OGRGeometry::OGR_G_3D,
2655 : GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED);
2656 : }
2657 18 : poGeomField->SetType(eType);
2658 18 : poGeomField->SetNullable(poGeomFieldIn->IsNullable());
2659 18 : poGeomField->nSRSId = nSRSId;
2660 18 : poGeomField->GeometryTypeFlags = GeometryTypeFlags;
2661 36 : poGeomField->ePostgisType = EQUAL(m_osLCOGeomType.c_str(), "geography")
2662 18 : ? GEOM_TYPE_GEOGRAPHY
2663 : : GEOM_TYPE_GEOMETRY;
2664 :
2665 : /* -------------------------------------------------------------------- */
2666 : /* Create the new field. */
2667 : /* -------------------------------------------------------------------- */
2668 18 : if (!bDeferredCreation)
2669 : {
2670 8 : poDS->EndCopy();
2671 :
2672 8 : if (RunAddGeometryColumn(poGeomField.get()) != OGRERR_NONE)
2673 : {
2674 0 : return OGRERR_FAILURE;
2675 : }
2676 :
2677 8 : if (bCreateSpatialIndexFlag)
2678 : {
2679 8 : if (RunCreateSpatialIndex(poGeomField.get(), 0) != OGRERR_NONE)
2680 : {
2681 0 : return OGRERR_FAILURE;
2682 : }
2683 : }
2684 : }
2685 :
2686 18 : whileUnsealing(poFeatureDefn)->AddGeomFieldDefn(std::move(poGeomField));
2687 :
2688 18 : return OGRERR_NONE;
2689 : }
2690 :
2691 : /************************************************************************/
2692 : /* DeleteField() */
2693 : /************************************************************************/
2694 :
2695 4 : OGRErr OGRPGTableLayer::DeleteField(int iField)
2696 : {
2697 4 : PGconn *hPGConn = poDS->GetPGConn();
2698 8 : CPLString osCommand;
2699 :
2700 4 : GetLayerDefn()->GetFieldCount();
2701 :
2702 4 : if (!bUpdateAccess)
2703 : {
2704 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2705 : "DeleteField");
2706 0 : return OGRERR_FAILURE;
2707 : }
2708 :
2709 4 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
2710 : {
2711 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2712 0 : return OGRERR_FAILURE;
2713 : }
2714 :
2715 4 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
2716 0 : return OGRERR_FAILURE;
2717 4 : poDS->EndCopy();
2718 :
2719 : osCommand.Printf(
2720 : "ALTER TABLE %s DROP COLUMN %s", pszSqlTableName,
2721 8 : OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef())
2722 4 : .c_str());
2723 4 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2724 4 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2725 : {
2726 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2727 : PQerrorMessage(hPGConn));
2728 :
2729 0 : OGRPGClearResult(hResult);
2730 :
2731 0 : return OGRERR_FAILURE;
2732 : }
2733 :
2734 4 : OGRPGClearResult(hResult);
2735 :
2736 4 : return whileUnsealing(poFeatureDefn)->DeleteFieldDefn(iField);
2737 : }
2738 :
2739 : /************************************************************************/
2740 : /* AlterFieldDefn() */
2741 : /************************************************************************/
2742 :
2743 20 : OGRErr OGRPGTableLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
2744 : int nFlagsIn)
2745 : {
2746 20 : PGconn *hPGConn = poDS->GetPGConn();
2747 40 : CPLString osCommand;
2748 :
2749 20 : GetLayerDefn()->GetFieldCount();
2750 :
2751 20 : if (!bUpdateAccess)
2752 : {
2753 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2754 : "AlterFieldDefn");
2755 0 : return OGRERR_FAILURE;
2756 : }
2757 :
2758 20 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
2759 : {
2760 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2761 0 : return OGRERR_FAILURE;
2762 : }
2763 :
2764 20 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
2765 0 : return OGRERR_FAILURE;
2766 20 : poDS->EndCopy();
2767 :
2768 20 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
2769 40 : auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
2770 40 : OGRFieldDefn oField(poNewFieldDefn);
2771 :
2772 20 : poDS->SoftStartTransaction();
2773 :
2774 20 : if (!(nFlagsIn & ALTER_TYPE_FLAG))
2775 : {
2776 14 : oField.SetSubType(OFSTNone);
2777 14 : oField.SetType(poFieldDefn->GetType());
2778 14 : oField.SetSubType(poFieldDefn->GetSubType());
2779 : }
2780 :
2781 20 : if (!(nFlagsIn & ALTER_WIDTH_PRECISION_FLAG))
2782 : {
2783 14 : oField.SetWidth(poFieldDefn->GetWidth());
2784 14 : oField.SetPrecision(poFieldDefn->GetPrecision());
2785 : }
2786 :
2787 20 : if ((nFlagsIn & ALTER_TYPE_FLAG) || (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG))
2788 : {
2789 : CPLString osFieldType = OGRPGCommonLayerGetType(
2790 6 : oField, CPL_TO_BOOL(bPreservePrecision), true);
2791 6 : if (osFieldType.empty())
2792 : {
2793 0 : poDS->SoftRollbackTransaction();
2794 :
2795 0 : return OGRERR_FAILURE;
2796 : }
2797 :
2798 : osCommand.Printf(
2799 : "ALTER TABLE %s ALTER COLUMN %s TYPE %s", pszSqlTableName,
2800 12 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2801 12 : osFieldType.c_str());
2802 :
2803 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2804 6 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2805 : {
2806 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2807 : PQerrorMessage(hPGConn));
2808 :
2809 0 : OGRPGClearResult(hResult);
2810 :
2811 0 : poDS->SoftRollbackTransaction();
2812 :
2813 0 : return OGRERR_FAILURE;
2814 : }
2815 6 : OGRPGClearResult(hResult);
2816 : }
2817 :
2818 26 : if ((nFlagsIn & ALTER_NULLABLE_FLAG) &&
2819 6 : poFieldDefn->IsNullable() != poNewFieldDefn->IsNullable())
2820 : {
2821 2 : oField.SetNullable(poNewFieldDefn->IsNullable());
2822 :
2823 2 : if (poNewFieldDefn->IsNullable())
2824 : osCommand.Printf(
2825 : "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", pszSqlTableName,
2826 1 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2827 : else
2828 : osCommand.Printf(
2829 : "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
2830 1 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2831 :
2832 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2833 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2834 : {
2835 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2836 : PQerrorMessage(hPGConn));
2837 :
2838 0 : OGRPGClearResult(hResult);
2839 :
2840 0 : poDS->SoftRollbackTransaction();
2841 :
2842 0 : return OGRERR_FAILURE;
2843 : }
2844 2 : OGRPGClearResult(hResult);
2845 : }
2846 :
2847 : // Only supports adding a unique constraint
2848 30 : if ((nFlagsIn & ALTER_UNIQUE_FLAG) && !poFieldDefn->IsUnique() &&
2849 10 : poNewFieldDefn->IsUnique())
2850 : {
2851 2 : oField.SetUnique(poNewFieldDefn->IsUnique());
2852 :
2853 : osCommand.Printf(
2854 : "ALTER TABLE %s ADD UNIQUE (%s)", pszSqlTableName,
2855 2 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2856 :
2857 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2858 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2859 : {
2860 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2861 : PQerrorMessage(hPGConn));
2862 :
2863 0 : OGRPGClearResult(hResult);
2864 :
2865 0 : poDS->SoftRollbackTransaction();
2866 :
2867 0 : return OGRERR_FAILURE;
2868 : }
2869 2 : OGRPGClearResult(hResult);
2870 : }
2871 22 : else if ((nFlagsIn & ALTER_UNIQUE_FLAG) && poFieldDefn->IsUnique() &&
2872 4 : !poNewFieldDefn->IsUnique())
2873 : {
2874 2 : oField.SetUnique(TRUE);
2875 2 : CPLError(CE_Warning, CPLE_NotSupported,
2876 : "Dropping a UNIQUE constraint is not supported currently");
2877 : }
2878 :
2879 28 : if ((nFlagsIn & ALTER_DEFAULT_FLAG) &&
2880 8 : ((poFieldDefn->GetDefault() == nullptr &&
2881 6 : poNewFieldDefn->GetDefault() != nullptr) ||
2882 8 : (poFieldDefn->GetDefault() != nullptr &&
2883 2 : poNewFieldDefn->GetDefault() == nullptr) ||
2884 7 : (poFieldDefn->GetDefault() != nullptr &&
2885 1 : poNewFieldDefn->GetDefault() != nullptr &&
2886 1 : strcmp(poFieldDefn->GetDefault(), poNewFieldDefn->GetDefault()) !=
2887 : 0)))
2888 : {
2889 2 : oField.SetDefault(poNewFieldDefn->GetDefault());
2890 :
2891 2 : if (poNewFieldDefn->GetDefault() == nullptr)
2892 : osCommand.Printf(
2893 : "ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT", pszSqlTableName,
2894 1 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2895 : else
2896 : osCommand.Printf(
2897 : "ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s",
2898 : pszSqlTableName,
2899 2 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2900 3 : OGRPGCommonLayerGetPGDefault(poNewFieldDefn).c_str());
2901 :
2902 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2903 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2904 : {
2905 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2906 : PQerrorMessage(hPGConn));
2907 :
2908 0 : OGRPGClearResult(hResult);
2909 :
2910 0 : poDS->SoftRollbackTransaction();
2911 :
2912 0 : return OGRERR_FAILURE;
2913 : }
2914 2 : OGRPGClearResult(hResult);
2915 : }
2916 :
2917 30 : if ((nFlagsIn & ALTER_COMMENT_FLAG) &&
2918 10 : poFieldDefn->GetComment() != poNewFieldDefn->GetComment())
2919 : {
2920 6 : oField.SetComment(poNewFieldDefn->GetComment());
2921 :
2922 6 : if (!poNewFieldDefn->GetComment().empty())
2923 : {
2924 : osCommand.Printf(
2925 : "COMMENT ON COLUMN %s.%s IS %s", pszSqlTableName,
2926 8 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2927 8 : OGRPGEscapeString(hPGConn, poNewFieldDefn->GetComment().c_str())
2928 8 : .c_str());
2929 : }
2930 : else
2931 : {
2932 : osCommand.Printf(
2933 : "COMMENT ON COLUMN %s.%s IS NULL", pszSqlTableName,
2934 2 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
2935 : }
2936 :
2937 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2938 6 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2939 : {
2940 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
2941 : PQerrorMessage(hPGConn));
2942 :
2943 0 : OGRPGClearResult(hResult);
2944 :
2945 0 : poDS->SoftRollbackTransaction();
2946 :
2947 0 : return OGRERR_FAILURE;
2948 : }
2949 6 : OGRPGClearResult(hResult);
2950 : }
2951 :
2952 20 : if ((nFlagsIn & ALTER_NAME_FLAG))
2953 : {
2954 6 : if (bLaunderColumnNames)
2955 : {
2956 6 : char *pszSafeName = OGRPGCommonLaunderName(oField.GetNameRef(),
2957 6 : "PG", m_bUTF8ToASCII);
2958 6 : oField.SetName(pszSafeName);
2959 6 : CPLFree(pszSafeName);
2960 : }
2961 :
2962 6 : if (EQUAL(oField.GetNameRef(), "oid"))
2963 : {
2964 0 : CPLError(CE_Warning, CPLE_AppDefined,
2965 : "Renaming field 'oid' to 'oid_' to avoid conflict with "
2966 : "internal oid field.");
2967 0 : oField.SetName("oid_");
2968 : }
2969 :
2970 6 : if (strcmp(poFieldDefn->GetNameRef(), oField.GetNameRef()) != 0)
2971 : {
2972 : osCommand.Printf(
2973 : "ALTER TABLE %s RENAME COLUMN %s TO %s", pszSqlTableName,
2974 12 : OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
2975 18 : OGRPGEscapeColumnName(oField.GetNameRef()).c_str());
2976 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
2977 6 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
2978 : {
2979 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s",
2980 : osCommand.c_str(), PQerrorMessage(hPGConn));
2981 :
2982 0 : OGRPGClearResult(hResult);
2983 :
2984 0 : poDS->SoftRollbackTransaction();
2985 :
2986 0 : return OGRERR_FAILURE;
2987 : }
2988 6 : OGRPGClearResult(hResult);
2989 : }
2990 : }
2991 :
2992 20 : poDS->SoftCommitTransaction();
2993 :
2994 20 : if (nFlagsIn & ALTER_NAME_FLAG)
2995 6 : poFieldDefn->SetName(oField.GetNameRef());
2996 20 : if (nFlagsIn & ALTER_TYPE_FLAG)
2997 : {
2998 6 : poFieldDefn->SetSubType(OFSTNone);
2999 6 : poFieldDefn->SetType(oField.GetType());
3000 6 : poFieldDefn->SetSubType(oField.GetSubType());
3001 : }
3002 20 : if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
3003 : {
3004 6 : poFieldDefn->SetWidth(oField.GetWidth());
3005 6 : poFieldDefn->SetPrecision(oField.GetPrecision());
3006 : }
3007 20 : if (nFlagsIn & ALTER_NULLABLE_FLAG)
3008 6 : poFieldDefn->SetNullable(oField.IsNullable());
3009 20 : if (nFlagsIn & ALTER_DEFAULT_FLAG)
3010 8 : poFieldDefn->SetDefault(oField.GetDefault());
3011 20 : if (nFlagsIn & ALTER_UNIQUE_FLAG)
3012 14 : poFieldDefn->SetUnique(oField.IsUnique());
3013 20 : if (nFlagsIn & ALTER_COMMENT_FLAG)
3014 10 : poFieldDefn->SetComment(oField.GetComment());
3015 :
3016 20 : return OGRERR_NONE;
3017 : }
3018 :
3019 : /************************************************************************/
3020 : /* AlterGeomFieldDefn() */
3021 : /************************************************************************/
3022 :
3023 : OGRErr
3024 6 : OGRPGTableLayer::AlterGeomFieldDefn(int iGeomFieldToAlter,
3025 : const OGRGeomFieldDefn *poNewGeomFieldDefn,
3026 : int nFlagsIn)
3027 : {
3028 6 : PGconn *hPGConn = poDS->GetPGConn();
3029 12 : CPLString osCommand;
3030 :
3031 6 : if (!bUpdateAccess)
3032 : {
3033 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3034 : "AlterGeomFieldDefn");
3035 0 : return OGRERR_FAILURE;
3036 : }
3037 :
3038 11 : if (iGeomFieldToAlter < 0 ||
3039 5 : iGeomFieldToAlter >= GetLayerDefn()->GetGeomFieldCount())
3040 : {
3041 1 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
3042 1 : return OGRERR_FAILURE;
3043 : }
3044 :
3045 5 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3046 0 : return OGRERR_FAILURE;
3047 5 : poDS->EndCopy();
3048 :
3049 5 : auto poGeomFieldDefn = cpl::down_cast<OGRPGGeomFieldDefn *>(
3050 5 : poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter));
3051 10 : auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
3052 :
3053 5 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG)
3054 : {
3055 5 : const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
3056 5 : if (poNewSRSRef && poNewSRSRef->GetCoordinateEpoch() > 0)
3057 : {
3058 1 : CPLError(CE_Failure, CPLE_NotSupported,
3059 : "Setting a coordinate epoch is not supported for "
3060 : "PostGIS");
3061 1 : return OGRERR_FAILURE;
3062 : }
3063 : }
3064 :
3065 8 : const OGRGeomFieldDefn oGeomField(poNewGeomFieldDefn);
3066 4 : poDS->SoftStartTransaction();
3067 :
3068 4 : int nGeomTypeFlags = poGeomFieldDefn->GeometryTypeFlags;
3069 :
3070 8 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG) &&
3071 4 : poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
3072 : {
3073 : const char *pszGeometryType =
3074 2 : OGRToOGCGeomType(poNewGeomFieldDefn->GetType());
3075 2 : std::string osType;
3076 2 : if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
3077 2 : osType += "geometry(";
3078 : else
3079 0 : osType += "geography(";
3080 2 : osType += pszGeometryType;
3081 2 : nGeomTypeFlags = 0;
3082 2 : if (OGR_GT_HasZ(poNewGeomFieldDefn->GetType()))
3083 0 : nGeomTypeFlags |= OGRGeometry::OGR_G_3D;
3084 2 : if (OGR_GT_HasM(poNewGeomFieldDefn->GetType()))
3085 0 : nGeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
3086 2 : if (nGeomTypeFlags & OGRGeometry::OGR_G_3D)
3087 0 : osType += "Z";
3088 2 : else if (nGeomTypeFlags & OGRGeometry::OGR_G_MEASURED)
3089 0 : osType += "M";
3090 2 : if (poGeomFieldDefn->nSRSId > 0)
3091 2 : osType += CPLSPrintf(",%d", poGeomFieldDefn->nSRSId);
3092 2 : osType += ")";
3093 :
3094 : osCommand.Printf(
3095 : "ALTER TABLE %s ALTER COLUMN %s TYPE %s", pszSqlTableName,
3096 4 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
3097 4 : osType.c_str());
3098 :
3099 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3100 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3101 : {
3102 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3103 : PQerrorMessage(hPGConn));
3104 :
3105 1 : OGRPGClearResult(hResult);
3106 :
3107 1 : poDS->SoftRollbackTransaction();
3108 :
3109 1 : return OGRERR_FAILURE;
3110 : }
3111 1 : OGRPGClearResult(hResult);
3112 : }
3113 :
3114 3 : const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
3115 3 : int nSRID = poGeomFieldDefn->nSRSId;
3116 :
3117 3 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG))
3118 : {
3119 3 : const auto poNewSRS = poNewGeomFieldDefn->GetSpatialRef();
3120 3 : const char *const apszOptions[] = {
3121 : "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
3122 3 : if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
3123 7 : (poOldSRS != nullptr && poNewSRS == nullptr) ||
3124 1 : (poOldSRS != nullptr && poNewSRS != nullptr &&
3125 1 : !poOldSRS->IsSame(poNewSRS, apszOptions)))
3126 : {
3127 3 : if (poNewSRS)
3128 2 : nSRID = poDS->FetchSRSId(poNewSRS);
3129 : else
3130 1 : nSRID = 0;
3131 :
3132 : osCommand.Printf(
3133 : "SELECT UpdateGeometrySRID(%s,%s,%s,%d)",
3134 6 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
3135 6 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
3136 6 : OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef())
3137 : .c_str(),
3138 9 : nSRID);
3139 :
3140 3 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3141 3 : if (PQresultStatus(hResult) != PGRES_TUPLES_OK)
3142 : {
3143 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s",
3144 : osCommand.c_str(), PQerrorMessage(hPGConn));
3145 :
3146 0 : OGRPGClearResult(hResult);
3147 :
3148 0 : poDS->SoftRollbackTransaction();
3149 :
3150 0 : return OGRERR_FAILURE;
3151 : }
3152 3 : OGRPGClearResult(hResult);
3153 : }
3154 : }
3155 :
3156 6 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG) &&
3157 3 : poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
3158 : {
3159 2 : if (poNewGeomFieldDefn->IsNullable())
3160 : osCommand.Printf(
3161 : "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", pszSqlTableName,
3162 1 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str());
3163 : else
3164 : osCommand.Printf(
3165 : "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
3166 1 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str());
3167 :
3168 2 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3169 2 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3170 : {
3171 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3172 : PQerrorMessage(hPGConn));
3173 :
3174 0 : OGRPGClearResult(hResult);
3175 :
3176 0 : poDS->SoftRollbackTransaction();
3177 :
3178 0 : return OGRERR_FAILURE;
3179 : }
3180 2 : OGRPGClearResult(hResult);
3181 : }
3182 :
3183 6 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) &&
3184 3 : strcmp(poGeomFieldDefn->GetNameRef(),
3185 : poNewGeomFieldDefn->GetNameRef()) != 0)
3186 : {
3187 : osCommand.Printf(
3188 : "ALTER TABLE %s RENAME COLUMN %s TO %s", pszSqlTableName,
3189 2 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
3190 3 : OGRPGEscapeColumnName(oGeomField.GetNameRef()).c_str());
3191 1 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3192 1 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3193 : {
3194 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3195 : PQerrorMessage(hPGConn));
3196 :
3197 0 : OGRPGClearResult(hResult);
3198 :
3199 0 : poDS->SoftRollbackTransaction();
3200 :
3201 0 : return OGRERR_FAILURE;
3202 : }
3203 1 : OGRPGClearResult(hResult);
3204 : }
3205 :
3206 3 : poDS->SoftCommitTransaction();
3207 :
3208 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG)
3209 3 : poGeomFieldDefn->SetName(oGeomField.GetNameRef());
3210 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
3211 : {
3212 3 : poGeomFieldDefn->GeometryTypeFlags = nGeomTypeFlags;
3213 3 : poGeomFieldDefn->SetType(oGeomField.GetType());
3214 : }
3215 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
3216 3 : poGeomFieldDefn->SetNullable(oGeomField.IsNullable());
3217 3 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG)
3218 : {
3219 3 : const auto poSRSRef = oGeomField.GetSpatialRef();
3220 3 : if (poSRSRef)
3221 : {
3222 2 : auto poSRSNew = poSRSRef->Clone();
3223 2 : poGeomFieldDefn->SetSpatialRef(poSRSNew);
3224 2 : poSRSNew->Release();
3225 : }
3226 : else
3227 : {
3228 1 : poGeomFieldDefn->SetSpatialRef(nullptr);
3229 : }
3230 3 : poGeomFieldDefn->nSRSId = nSRID;
3231 : }
3232 :
3233 3 : return OGRERR_NONE;
3234 : }
3235 :
3236 : /************************************************************************/
3237 : /* GetFeature() */
3238 : /************************************************************************/
3239 :
3240 146 : OGRFeature *OGRPGTableLayer::GetFeature(GIntBig nFeatureId)
3241 :
3242 : {
3243 146 : GetLayerDefn()->GetFieldCount();
3244 :
3245 146 : if (pszFIDColumn == nullptr)
3246 3 : return OGRLayer::GetFeature(nFeatureId);
3247 :
3248 : /* -------------------------------------------------------------------- */
3249 : /* Issue query for a single record. */
3250 : /* -------------------------------------------------------------------- */
3251 143 : OGRFeature *poFeature = nullptr;
3252 143 : PGconn *hPGConn = poDS->GetPGConn();
3253 286 : CPLString osFieldList = BuildFields();
3254 143 : CPLString osCommand;
3255 :
3256 143 : poDS->EndCopy();
3257 143 : poDS->SoftStartTransaction();
3258 :
3259 : osCommand.Printf("DECLARE getfeaturecursor %s for "
3260 : "SELECT %s FROM %s WHERE %s = " CPL_FRMT_GIB,
3261 143 : (poDS->bUseBinaryCursor) ? "BINARY CURSOR" : "CURSOR",
3262 : osFieldList.c_str(), pszSqlTableName,
3263 143 : OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFeatureId);
3264 :
3265 143 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
3266 :
3267 143 : if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
3268 : {
3269 143 : OGRPGClearResult(hResult);
3270 :
3271 143 : hResult = OGRPG_PQexec(hPGConn, "FETCH ALL in getfeaturecursor");
3272 :
3273 143 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
3274 : {
3275 143 : int nRows = PQntuples(hResult);
3276 143 : if (nRows > 0)
3277 : {
3278 121 : int *panTempMapFieldNameToIndex = nullptr;
3279 121 : int *panTempMapFieldNameToGeomIndex = nullptr;
3280 121 : CreateMapFromFieldNameToIndex(hResult, poFeatureDefn,
3281 : panTempMapFieldNameToIndex,
3282 : panTempMapFieldNameToGeomIndex);
3283 121 : poFeature = RecordToFeature(hResult, panTempMapFieldNameToIndex,
3284 : panTempMapFieldNameToGeomIndex, 0);
3285 121 : CPLFree(panTempMapFieldNameToIndex);
3286 121 : CPLFree(panTempMapFieldNameToGeomIndex);
3287 121 : if (poFeature && iFIDAsRegularColumnIndex >= 0)
3288 : {
3289 1 : poFeature->SetField(iFIDAsRegularColumnIndex,
3290 : poFeature->GetFID());
3291 : }
3292 :
3293 121 : if (nRows > 1)
3294 : {
3295 0 : CPLError(
3296 : CE_Warning, CPLE_AppDefined,
3297 : "%d rows in response to the WHERE %s = " CPL_FRMT_GIB
3298 : " clause !",
3299 : nRows, pszFIDColumn, nFeatureId);
3300 : }
3301 : }
3302 : else
3303 : {
3304 22 : CPLError(CE_Failure, CPLE_AppDefined,
3305 : "Attempt to read feature with unknown feature id "
3306 : "(" CPL_FRMT_GIB ").",
3307 : nFeatureId);
3308 : }
3309 : }
3310 : }
3311 0 : else if (hResult && PQresultStatus(hResult) == PGRES_FATAL_ERROR)
3312 : {
3313 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
3314 : PQresultErrorMessage(hResult));
3315 : }
3316 :
3317 : /* -------------------------------------------------------------------- */
3318 : /* Cleanup */
3319 : /* -------------------------------------------------------------------- */
3320 143 : OGRPGClearResult(hResult);
3321 :
3322 143 : hResult = OGRPG_PQexec(hPGConn, "CLOSE getfeaturecursor");
3323 143 : OGRPGClearResult(hResult);
3324 :
3325 143 : poDS->SoftCommitTransaction();
3326 :
3327 143 : return poFeature;
3328 : }
3329 :
3330 : /************************************************************************/
3331 : /* GetFeatureCount() */
3332 : /************************************************************************/
3333 :
3334 152 : GIntBig OGRPGTableLayer::GetFeatureCount(int bForce)
3335 :
3336 : {
3337 152 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3338 0 : return 0;
3339 152 : poDS->EndCopy();
3340 :
3341 152 : if (TestCapability(OLCFastFeatureCount) == FALSE)
3342 11 : return OGRPGLayer::GetFeatureCount(bForce);
3343 :
3344 : /* -------------------------------------------------------------------- */
3345 : /* In theory it might be wise to cache this result, but it */
3346 : /* won't be trivial to work out the lifetime of the value. */
3347 : /* After all someone else could be adding records from another */
3348 : /* application when working against a database. */
3349 : /* -------------------------------------------------------------------- */
3350 141 : PGconn *hPGConn = poDS->GetPGConn();
3351 141 : CPLString osCommand;
3352 141 : GIntBig nCount = 0;
3353 :
3354 : osCommand.Printf("SELECT count(*) FROM %s %s", pszSqlTableName,
3355 141 : osWHERE.c_str());
3356 :
3357 141 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3358 141 : if (hResult != nullptr && PQresultStatus(hResult) == PGRES_TUPLES_OK)
3359 141 : nCount = CPLAtoGIntBig(PQgetvalue(hResult, 0, 0));
3360 : else
3361 0 : CPLDebug("PG", "%s; failed.", osCommand.c_str());
3362 141 : OGRPGClearResult(hResult);
3363 :
3364 141 : return nCount;
3365 : }
3366 :
3367 : /************************************************************************/
3368 : /* ResolveSRID() */
3369 : /************************************************************************/
3370 :
3371 83 : void OGRPGTableLayer::ResolveSRID(const OGRPGGeomFieldDefn *poGFldDefn)
3372 :
3373 : {
3374 83 : PGconn *hPGConn = poDS->GetPGConn();
3375 83 : CPLString osCommand;
3376 :
3377 83 : int nSRSId = poDS->GetUndefinedSRID();
3378 83 : if (!poDS->m_bHasGeometryColumns)
3379 : {
3380 0 : poGFldDefn->nSRSId = nSRSId;
3381 0 : return;
3382 : }
3383 :
3384 : osCommand.Printf(
3385 : "SELECT srid FROM geometry_columns "
3386 : "WHERE f_table_name = %s AND "
3387 : "f_geometry_column = %s",
3388 166 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
3389 249 : OGRPGEscapeString(hPGConn, poGFldDefn->GetNameRef()).c_str());
3390 :
3391 : osCommand +=
3392 83 : CPLString().Printf(" AND f_table_schema = %s",
3393 83 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
3394 :
3395 83 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
3396 :
3397 166 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
3398 83 : PQntuples(hResult) == 1)
3399 : {
3400 39 : nSRSId = atoi(PQgetvalue(hResult, 0, 0));
3401 : }
3402 :
3403 83 : OGRPGClearResult(hResult);
3404 :
3405 : /* With PostGIS 2.0, SRID = 0 can also mean that there's no constraint */
3406 : /* so we need to fetch from values */
3407 : /* We assume that all geometry of this column have identical SRID */
3408 83 : if (nSRSId <= 0 && poGFldDefn->ePostgisType == GEOM_TYPE_GEOMETRY &&
3409 39 : poDS->sPostGISVersion.nMajor >= 0)
3410 : {
3411 72 : CPLString osGetSRID;
3412 36 : osGetSRID += "SELECT ST_SRID(";
3413 36 : osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef());
3414 36 : osGetSRID += ") FROM ";
3415 36 : osGetSRID += pszSqlTableName;
3416 36 : osGetSRID += " WHERE (";
3417 36 : osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef());
3418 36 : osGetSRID += " IS NOT NULL) LIMIT 1";
3419 :
3420 36 : hResult = OGRPG_PQexec(poDS->GetPGConn(), osGetSRID);
3421 72 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
3422 36 : PQntuples(hResult) == 1)
3423 : {
3424 34 : nSRSId = atoi(PQgetvalue(hResult, 0, 0));
3425 : }
3426 :
3427 36 : OGRPGClearResult(hResult);
3428 : }
3429 :
3430 83 : poGFldDefn->nSRSId = nSRSId;
3431 : }
3432 :
3433 : /************************************************************************/
3434 : /* StartCopy() */
3435 : /************************************************************************/
3436 :
3437 185 : OGRErr OGRPGTableLayer::StartCopy()
3438 :
3439 : {
3440 : /*CPLDebug("PG", "OGRPGDataSource(%p)::StartCopy(%p)", poDS, this);*/
3441 :
3442 185 : CPLString osFields = BuildCopyFields();
3443 :
3444 185 : size_t size = osFields.size() + strlen(pszSqlTableName) + 100;
3445 185 : char *pszCommand = static_cast<char *>(CPLMalloc(size));
3446 :
3447 185 : snprintf(pszCommand, size, "COPY %s (%s) FROM STDIN;", pszSqlTableName,
3448 : osFields.c_str());
3449 :
3450 185 : PGconn *hPGConn = poDS->GetPGConn();
3451 185 : PGresult *hResult = OGRPG_PQexec(hPGConn, pszCommand);
3452 :
3453 185 : if (!hResult || (PQresultStatus(hResult) != PGRES_COPY_IN))
3454 : {
3455 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
3456 : }
3457 : else
3458 185 : bCopyActive = TRUE;
3459 :
3460 185 : OGRPGClearResult(hResult);
3461 185 : CPLFree(pszCommand);
3462 :
3463 370 : return OGRERR_NONE;
3464 : }
3465 :
3466 : /************************************************************************/
3467 : /* EndCopy() */
3468 : /************************************************************************/
3469 :
3470 185 : OGRErr OGRPGTableLayer::EndCopy()
3471 :
3472 : {
3473 185 : if (!bCopyActive)
3474 0 : return OGRERR_NONE;
3475 : /*CPLDebug("PG", "OGRPGDataSource(%p)::EndCopy(%p)", poDS, this);*/
3476 :
3477 : /* This method is called from the datasource when
3478 : a COPY operation is ended */
3479 185 : OGRErr result = OGRERR_NONE;
3480 :
3481 185 : PGconn *hPGConn = poDS->GetPGConn();
3482 185 : CPLDebug("PG", "PQputCopyEnd()");
3483 :
3484 185 : bCopyActive = FALSE;
3485 :
3486 185 : int copyResult = PQputCopyEnd(hPGConn, nullptr);
3487 :
3488 185 : switch (copyResult)
3489 : {
3490 0 : case 0:
3491 0 : CPLError(CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
3492 0 : result = OGRERR_FAILURE;
3493 0 : break;
3494 0 : case -1:
3495 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
3496 : PQerrorMessage(hPGConn));
3497 0 : result = OGRERR_FAILURE;
3498 0 : break;
3499 : }
3500 :
3501 : /* Now check the results of the copy */
3502 185 : PGresult *hResult = PQgetResult(hPGConn);
3503 :
3504 185 : if (hResult && PQresultStatus(hResult) != PGRES_COMMAND_OK)
3505 : {
3506 1 : CPLError(CE_Failure, CPLE_AppDefined, "COPY statement failed.\n%s",
3507 : PQerrorMessage(hPGConn));
3508 :
3509 1 : result = OGRERR_FAILURE;
3510 : }
3511 :
3512 185 : OGRPGClearResult(hResult);
3513 :
3514 185 : if (!bUseCopyByDefault)
3515 34 : bUseCopy = USE_COPY_UNSET;
3516 :
3517 185 : UpdateSequenceIfNeeded();
3518 :
3519 185 : return result;
3520 : }
3521 :
3522 : /************************************************************************/
3523 : /* UpdateSequenceIfNeeded() */
3524 : /************************************************************************/
3525 :
3526 1035 : void OGRPGTableLayer::UpdateSequenceIfNeeded()
3527 : {
3528 1035 : if (bNeedToUpdateSequence && pszFIDColumn != nullptr)
3529 : {
3530 18 : PGconn *hPGConn = poDS->GetPGConn();
3531 18 : CPLString osCommand;
3532 : // setval() only works if the value is in [1,INT_MAX] range
3533 : // so do not update it if MAX(fid) <= 0
3534 : osCommand.Printf(
3535 : "SELECT setval(pg_get_serial_sequence(%s, %s), MAX(%s)) FROM %s "
3536 : "WHERE EXISTS (SELECT 1 FROM %s WHERE %s > 0 LIMIT 1)",
3537 36 : OGRPGEscapeString(hPGConn, pszSqlTableName).c_str(),
3538 36 : OGRPGEscapeString(hPGConn, pszFIDColumn).c_str(),
3539 18 : OGRPGEscapeColumnName(pszFIDColumn).c_str(), pszSqlTableName,
3540 72 : pszSqlTableName, OGRPGEscapeColumnName(pszFIDColumn).c_str());
3541 18 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3542 18 : OGRPGClearResult(hResult);
3543 18 : bNeedToUpdateSequence = false;
3544 : }
3545 1035 : }
3546 :
3547 : /************************************************************************/
3548 : /* BuildCopyFields() */
3549 : /************************************************************************/
3550 :
3551 185 : CPLString OGRPGTableLayer::BuildCopyFields()
3552 : {
3553 185 : int i = 0;
3554 185 : int nFIDIndex = -1;
3555 185 : CPLString osFieldList;
3556 :
3557 354 : for (i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
3558 : {
3559 169 : OGRGeomFieldDefn *poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(i);
3560 169 : if (!osFieldList.empty())
3561 6 : osFieldList += ", ";
3562 169 : osFieldList += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
3563 : }
3564 :
3565 185 : if (bFIDColumnInCopyFields)
3566 : {
3567 11 : if (!osFieldList.empty())
3568 7 : osFieldList += ", ";
3569 :
3570 11 : nFIDIndex = poFeatureDefn->GetFieldIndex(pszFIDColumn);
3571 :
3572 11 : osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
3573 : }
3574 :
3575 708 : for (i = 0; i < poFeatureDefn->GetFieldCount(); i++)
3576 : {
3577 523 : if (i == nFIDIndex)
3578 2 : continue;
3579 :
3580 521 : if (poFeatureDefn->GetFieldDefn(i)->IsGenerated())
3581 2 : continue;
3582 :
3583 519 : const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
3584 :
3585 519 : if (!osFieldList.empty())
3586 501 : osFieldList += ", ";
3587 :
3588 519 : osFieldList += OGRPGEscapeColumnName(pszName);
3589 : }
3590 :
3591 185 : return osFieldList;
3592 : }
3593 :
3594 : /************************************************************************/
3595 : /* CheckGeomTypeCompatibility() */
3596 : /************************************************************************/
3597 :
3598 1702 : void OGRPGTableLayer::CheckGeomTypeCompatibility(int iGeomField,
3599 : OGRGeometry *poGeom)
3600 : {
3601 1702 : if (bHasWarnedIncompatibleGeom)
3602 0 : return;
3603 :
3604 : OGRwkbGeometryType eExpectedGeomType =
3605 1702 : poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetType();
3606 1702 : OGRwkbGeometryType eFlatLayerGeomType = wkbFlatten(eExpectedGeomType);
3607 1702 : OGRwkbGeometryType eFlatGeomType = wkbFlatten(poGeom->getGeometryType());
3608 1702 : if (eFlatLayerGeomType == wkbUnknown)
3609 676 : return;
3610 :
3611 1026 : if (eFlatLayerGeomType == wkbGeometryCollection)
3612 0 : bHasWarnedIncompatibleGeom = eFlatGeomType != wkbMultiPoint &&
3613 0 : eFlatGeomType != wkbMultiLineString &&
3614 0 : eFlatGeomType != wkbMultiPolygon &&
3615 : eFlatGeomType != wkbGeometryCollection;
3616 : else
3617 1026 : bHasWarnedIncompatibleGeom = (eFlatGeomType != eFlatLayerGeomType);
3618 :
3619 1026 : if (bHasWarnedIncompatibleGeom)
3620 : {
3621 2 : CPLError(CE_Warning, CPLE_AppDefined,
3622 : "Geometry to be inserted is of type %s, whereas the layer "
3623 : "geometry type is %s.\n"
3624 : "Insertion is likely to fail",
3625 1 : OGRGeometryTypeToName(poGeom->getGeometryType()),
3626 : OGRGeometryTypeToName(eExpectedGeomType));
3627 : }
3628 : }
3629 :
3630 : /************************************************************************/
3631 : /* SetOverrideColumnTypes() */
3632 : /************************************************************************/
3633 :
3634 223 : void OGRPGTableLayer::SetOverrideColumnTypes(const char *pszOverrideColumnTypes)
3635 : {
3636 223 : if (pszOverrideColumnTypes == nullptr)
3637 222 : return;
3638 :
3639 1 : const char *pszIter = pszOverrideColumnTypes;
3640 2 : CPLString osCur;
3641 44 : while (*pszIter != '\0')
3642 : {
3643 44 : if (*pszIter == '(')
3644 : {
3645 : /* Ignore commas inside ( ) pair */
3646 12 : while (*pszIter != '\0')
3647 : {
3648 12 : if (*pszIter == ')')
3649 : {
3650 2 : osCur += *pszIter;
3651 2 : pszIter++;
3652 2 : break;
3653 : }
3654 10 : osCur += *pszIter;
3655 10 : pszIter++;
3656 : }
3657 2 : if (*pszIter == '\0')
3658 1 : break;
3659 : }
3660 :
3661 43 : if (*pszIter == ',')
3662 : {
3663 3 : papszOverrideColumnTypes =
3664 3 : CSLAddString(papszOverrideColumnTypes, osCur);
3665 3 : osCur = "";
3666 : }
3667 : else
3668 40 : osCur += *pszIter;
3669 43 : pszIter++;
3670 : }
3671 1 : if (!osCur.empty())
3672 1 : papszOverrideColumnTypes =
3673 1 : CSLAddString(papszOverrideColumnTypes, osCur);
3674 : }
3675 :
3676 : /************************************************************************/
3677 : /* IGetExtent() */
3678 : /* */
3679 : /* For PostGIS use internal ST_EstimatedExtent(geometry) function */
3680 : /* if bForce == 0 */
3681 : /************************************************************************/
3682 :
3683 22 : OGRErr OGRPGTableLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
3684 : bool bForce)
3685 : {
3686 44 : CPLString osCommand;
3687 :
3688 22 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3689 0 : return OGRERR_FAILURE;
3690 22 : poDS->EndCopy();
3691 :
3692 : OGRPGGeomFieldDefn *poGeomFieldDefn =
3693 22 : poFeatureDefn->GetGeomFieldDefn(iGeomField);
3694 :
3695 : // if bForce is 0 and ePostgisType is not GEOM_TYPE_GEOGRAPHY we can use
3696 : // the ST_EstimatedExtent function which is quicker
3697 : // ST_EstimatedExtent was called ST_Estimated_Extent up to PostGIS 2.0.x
3698 : // ST_EstimatedExtent returns NULL in absence of statistics (an exception
3699 : // before
3700 : // PostGIS 1.5.4)
3701 22 : if (bForce == 0 && TestCapability(OLCFastGetExtent))
3702 : {
3703 1 : PGconn *hPGConn = poDS->GetPGConn();
3704 :
3705 2 : const char *pszExtentFct = poDS->sPostGISVersion.nMajor > 2 ||
3706 0 : (poDS->sPostGISVersion.nMajor == 2 &&
3707 0 : poDS->sPostGISVersion.nMinor >= 1)
3708 1 : ? "ST_EstimatedExtent"
3709 : : "ST_Estimated_Extent";
3710 :
3711 : osCommand.Printf(
3712 : "SELECT %s(%s, %s, %s)", pszExtentFct,
3713 2 : OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
3714 2 : OGRPGEscapeString(hPGConn, pszTableName).c_str(),
3715 4 : OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef()).c_str());
3716 :
3717 : /* Quiet error: ST_Estimated_Extent may return an error if statistics */
3718 : /* have not been computed */
3719 1 : if (RunGetExtentRequest(*psExtent, bForce, osCommand, TRUE) ==
3720 : OGRERR_NONE)
3721 1 : return OGRERR_NONE;
3722 :
3723 0 : CPLDebug(
3724 : "PG",
3725 : "Unable to get estimated extent by PostGIS. Trying real extent.");
3726 : }
3727 :
3728 21 : return OGRPGLayer::IGetExtent(iGeomField, psExtent, bForce);
3729 : }
3730 :
3731 : /************************************************************************/
3732 : /* Rename() */
3733 : /************************************************************************/
3734 :
3735 6 : OGRErr OGRPGTableLayer::Rename(const char *pszNewName)
3736 : {
3737 6 : if (!TestCapability(OLCRename))
3738 0 : return OGRERR_FAILURE;
3739 :
3740 6 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3741 0 : return OGRERR_FAILURE;
3742 6 : poDS->EndCopy();
3743 6 : ResetReading();
3744 :
3745 6 : char *pszNewSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszNewName));
3746 6 : PGconn *hPGConn = poDS->GetPGConn();
3747 6 : CPLString osCommand;
3748 : osCommand.Printf("ALTER TABLE %s RENAME TO %s", pszSqlTableName,
3749 6 : pszNewSqlTableName);
3750 6 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
3751 :
3752 6 : OGRErr eRet = OGRERR_NONE;
3753 6 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
3754 : {
3755 2 : eRet = OGRERR_FAILURE;
3756 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
3757 :
3758 2 : CPLFree(pszNewSqlTableName);
3759 : }
3760 : else
3761 : {
3762 4 : CPLFree(pszTableName);
3763 4 : pszTableName = CPLStrdup(pszNewName);
3764 :
3765 4 : CPLFree(pszSqlTableName);
3766 4 : pszSqlTableName = pszNewSqlTableName;
3767 :
3768 4 : SetDescription(pszNewName);
3769 4 : whileUnsealing(poFeatureDefn)->SetName(pszNewName);
3770 : }
3771 :
3772 6 : OGRPGClearResult(hResult);
3773 :
3774 6 : return eRet;
3775 : }
3776 :
3777 : /************************************************************************/
3778 : /* SetDeferredCreation() */
3779 : /************************************************************************/
3780 :
3781 223 : void OGRPGTableLayer::SetDeferredCreation(int bDeferredCreationIn,
3782 : const std::string &osCreateTableIn)
3783 : {
3784 223 : bDeferredCreation = bDeferredCreationIn;
3785 223 : osCreateTable = osCreateTableIn;
3786 223 : }
3787 :
3788 : /************************************************************************/
3789 : /* RunDeferredCreationIfNecessary() */
3790 : /************************************************************************/
3791 :
3792 1525 : OGRErr OGRPGTableLayer::RunDeferredCreationIfNecessary()
3793 : {
3794 1525 : if (!bDeferredCreation)
3795 1393 : return OGRERR_NONE;
3796 132 : bDeferredCreation = FALSE;
3797 :
3798 132 : poDS->EndCopy();
3799 :
3800 253 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
3801 : {
3802 121 : OGRPGGeomFieldDefn *poGeomField = poFeatureDefn->GetGeomFieldDefn(i);
3803 :
3804 121 : if (poDS->HavePostGIS() ||
3805 0 : poGeomField->ePostgisType == GEOM_TYPE_GEOGRAPHY)
3806 : {
3807 : const char *pszGeometryType =
3808 121 : OGRToOGCGeomType(poGeomField->GetType());
3809 :
3810 121 : osCreateTable += ", ";
3811 121 : osCreateTable += OGRPGEscapeColumnName(poGeomField->GetNameRef());
3812 121 : osCreateTable += " ";
3813 121 : if (poGeomField->ePostgisType == GEOM_TYPE_GEOMETRY)
3814 117 : osCreateTable += "geometry(";
3815 : else
3816 4 : osCreateTable += "geography(";
3817 121 : osCreateTable += pszGeometryType;
3818 121 : if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
3819 46 : (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
3820 3 : osCreateTable += "ZM";
3821 118 : else if (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D)
3822 43 : osCreateTable += "Z";
3823 75 : else if (poGeomField->GeometryTypeFlags &
3824 : OGRGeometry::OGR_G_MEASURED)
3825 4 : osCreateTable += "M";
3826 121 : if (poGeomField->nSRSId > 0)
3827 16 : osCreateTable += CPLSPrintf(",%d", poGeomField->nSRSId);
3828 121 : osCreateTable += ")";
3829 121 : if (!poGeomField->IsNullable())
3830 1 : osCreateTable += " NOT NULL";
3831 : }
3832 : }
3833 :
3834 132 : osCreateTable += " )";
3835 264 : CPLString osCommand(osCreateTable);
3836 :
3837 132 : PGconn *hPGConn = poDS->GetPGConn();
3838 :
3839 132 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
3840 132 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
3841 : {
3842 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
3843 : PQerrorMessage(hPGConn));
3844 :
3845 0 : OGRPGClearResult(hResult);
3846 0 : return OGRERR_FAILURE;
3847 : }
3848 :
3849 132 : OGRPGClearResult(hResult);
3850 :
3851 133 : for (const auto &osSQL : m_aosDeferredCommentOnColumns)
3852 : {
3853 1 : hResult = OGRPG_PQexec(hPGConn, osSQL.c_str());
3854 1 : OGRPGClearResult(hResult);
3855 : }
3856 132 : m_aosDeferredCommentOnColumns.clear();
3857 :
3858 132 : if (bCreateSpatialIndexFlag)
3859 : {
3860 253 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
3861 : {
3862 : OGRPGGeomFieldDefn *poGeomField =
3863 121 : poFeatureDefn->GetGeomFieldDefn(i);
3864 121 : if (RunCreateSpatialIndex(poGeomField, i) != OGRERR_NONE)
3865 : {
3866 0 : return OGRERR_FAILURE;
3867 : }
3868 : }
3869 : }
3870 :
3871 132 : char **papszMD = OGRLayer::GetMetadata();
3872 132 : if (papszMD != nullptr)
3873 2 : SetMetadata(papszMD);
3874 :
3875 132 : return OGRERR_NONE;
3876 : }
3877 :
3878 : /************************************************************************/
3879 : /* GetGeometryTypes() */
3880 : /************************************************************************/
3881 :
3882 27 : OGRGeometryTypeCounter *OGRPGTableLayer::GetGeometryTypes(
3883 : int iGeomField, int nFlagsGGT, int &nEntryCountOut,
3884 : GDALProgressFunc pfnProgress, void *pProgressData)
3885 : {
3886 27 : if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount())
3887 : {
3888 1 : CPLError(CE_Failure, CPLE_AppDefined,
3889 : "Invalid geometry field index : %d", iGeomField);
3890 1 : nEntryCountOut = 0;
3891 1 : return nullptr;
3892 : }
3893 :
3894 26 : if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3895 : {
3896 0 : nEntryCountOut = 0;
3897 0 : return nullptr;
3898 : }
3899 26 : poDS->EndCopy();
3900 :
3901 : const OGRPGGeomFieldDefn *poGeomFieldDefn =
3902 26 : GetLayerDefn()->GetGeomFieldDefn(iGeomField);
3903 : const auto osEscapedGeom =
3904 52 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
3905 52 : CPLString osSQL;
3906 26 : if ((nFlagsGGT & OGR_GGT_GEOMCOLLECTIONZ_TINZ) != 0)
3907 : {
3908 18 : CPLString osFilter;
3909 : osFilter.Printf("(ST_Zmflag(%s) = 2 AND "
3910 : "((GeometryType(%s) = 'GEOMETRYCOLLECTION' AND "
3911 : "ST_NumGeometries(%s) >= 1 AND "
3912 : "geometrytype(ST_GeometryN(%s, 1)) = 'TIN') OR "
3913 : "GeometryType(%s) = 'TIN'))",
3914 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3915 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3916 9 : osEscapedGeom.c_str());
3917 :
3918 18 : std::string l_osWHERE(osWHERE);
3919 9 : if (l_osWHERE.empty())
3920 6 : l_osWHERE = " WHERE ";
3921 : else
3922 3 : l_osWHERE += " AND ";
3923 9 : l_osWHERE += "(NOT (";
3924 9 : l_osWHERE += osFilter;
3925 9 : l_osWHERE += ") OR ";
3926 9 : l_osWHERE += osEscapedGeom;
3927 9 : l_osWHERE += " IS NULL)";
3928 :
3929 18 : std::string l_osWHEREFilter(osWHERE);
3930 9 : if (l_osWHEREFilter.empty())
3931 6 : l_osWHEREFilter = " WHERE ";
3932 : else
3933 3 : l_osWHEREFilter += " AND ";
3934 9 : l_osWHEREFilter += osFilter;
3935 :
3936 : osSQL.Printf(
3937 : "(SELECT GeometryType(%s), ST_Zmflag(%s), COUNT(*) FROM %s %s "
3938 : "GROUP BY GeometryType(%s), ST_Zmflag(%s)) UNION ALL "
3939 : "(SELECT * FROM (SELECT 'TIN', 2, COUNT(*) AS count FROM %s %s) "
3940 : "tinsubselect WHERE tinsubselect.count != 0)",
3941 : osEscapedGeom.c_str(), osEscapedGeom.c_str(), pszSqlTableName,
3942 : l_osWHERE.c_str(), osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3943 9 : pszSqlTableName, l_osWHEREFilter.c_str());
3944 : }
3945 17 : else if ((nFlagsGGT & OGR_GGT_STOP_IF_MIXED) != 0)
3946 : {
3947 8 : std::string l_osWHERE(osWHERE);
3948 4 : if (l_osWHERE.empty())
3949 2 : l_osWHERE = " WHERE ";
3950 : else
3951 2 : l_osWHERE += " AND ";
3952 4 : l_osWHERE += osEscapedGeom;
3953 4 : l_osWHERE += " IS NOT NULL";
3954 :
3955 8 : std::string l_osWHERE_NULL(osWHERE);
3956 4 : if (l_osWHERE_NULL.empty())
3957 2 : l_osWHERE_NULL = " WHERE ";
3958 : else
3959 2 : l_osWHERE_NULL += " AND ";
3960 4 : l_osWHERE_NULL += osEscapedGeom;
3961 4 : l_osWHERE_NULL += " IS NULL";
3962 :
3963 : osSQL.Printf("(SELECT DISTINCT GeometryType(%s), ST_Zmflag(%s), 0 FROM "
3964 : "%s %s LIMIT 2) "
3965 : "UNION ALL (SELECT NULL, NULL, 0 FROM %s %s LIMIT 1)",
3966 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3967 : pszSqlTableName, l_osWHERE.c_str(), pszSqlTableName,
3968 4 : l_osWHERE_NULL.c_str());
3969 : }
3970 : else
3971 : {
3972 : const bool bDebug =
3973 13 : CPLTestBool(CPLGetConfigOption("OGR_PG_DEBUG_GGT_CANCEL", "NO"));
3974 : osSQL.Printf(
3975 : "SELECT GeometryType(%s), ST_Zmflag(%s), COUNT(*)%s FROM %s %s "
3976 : "GROUP BY GeometryType(%s), ST_Zmflag(%s)",
3977 : osEscapedGeom.c_str(), osEscapedGeom.c_str(),
3978 : bDebug ? ", pg_sleep(1)" : "", pszSqlTableName, osWHERE.c_str(),
3979 13 : osEscapedGeom.c_str(), osEscapedGeom.c_str());
3980 : }
3981 :
3982 52 : std::thread thread;
3983 26 : std::mutex mutex;
3984 26 : std::condition_variable cv;
3985 26 : bool stopThread = false;
3986 26 : if (pfnProgress && pfnProgress != GDALDummyProgress)
3987 : {
3988 4 : thread = std::thread(
3989 2 : [&]()
3990 : {
3991 4 : std::unique_lock<std::mutex> lock(mutex);
3992 4 : while (!stopThread)
3993 : {
3994 2 : if (!pfnProgress(0.0, "", pProgressData))
3995 1 : poDS->AbortSQL();
3996 2 : cv.wait_for(lock, std::chrono::milliseconds(100));
3997 : }
3998 4 : });
3999 : }
4000 :
4001 26 : PGconn *hPGConn = poDS->GetPGConn();
4002 26 : PGresult *hResult = OGRPG_PQexec(hPGConn, osSQL.c_str());
4003 :
4004 26 : if (pfnProgress && pfnProgress != GDALDummyProgress)
4005 : {
4006 : {
4007 4 : std::unique_lock<std::mutex> lock(mutex);
4008 2 : stopThread = true;
4009 2 : cv.notify_one();
4010 : }
4011 2 : thread.join();
4012 : }
4013 :
4014 26 : nEntryCountOut = 0;
4015 26 : OGRGeometryTypeCounter *pasRet = nullptr;
4016 26 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
4017 : {
4018 25 : const int nTuples = PQntuples(hResult);
4019 25 : nEntryCountOut = nTuples;
4020 : pasRet = static_cast<OGRGeometryTypeCounter *>(
4021 25 : CPLCalloc(1 + nEntryCountOut, sizeof(OGRGeometryTypeCounter)));
4022 69 : for (int i = 0; i < nTuples; ++i)
4023 : {
4024 44 : const char *pszGeomType = PQgetvalue(hResult, i, 0);
4025 44 : const char *pszZMFlag = PQgetvalue(hResult, i, 1);
4026 44 : const char *pszCount = PQgetvalue(hResult, i, 2);
4027 44 : if (pszCount)
4028 : {
4029 44 : if (pszGeomType == nullptr || pszGeomType[0] == '\0')
4030 : {
4031 16 : pasRet[i].eGeomType = wkbNone;
4032 : }
4033 28 : else if (pszZMFlag != nullptr)
4034 : {
4035 28 : const int nZMFlag = atoi(pszZMFlag);
4036 28 : pasRet[i].eGeomType = OGRFromOGCGeomType(pszGeomType);
4037 28 : int nModifier = 0;
4038 28 : if (nZMFlag == 1)
4039 1 : nModifier = OGRGeometry::OGR_G_MEASURED;
4040 27 : else if (nZMFlag == 2)
4041 9 : nModifier = OGRGeometry::OGR_G_3D;
4042 18 : else if (nZMFlag == 3)
4043 1 : nModifier =
4044 : OGRGeometry::OGR_G_MEASURED | OGRGeometry::OGR_G_3D;
4045 28 : pasRet[i].eGeomType = OGR_GT_SetModifier(
4046 28 : pasRet[i].eGeomType, nModifier & OGRGeometry::OGR_G_3D,
4047 : nModifier & OGRGeometry::OGR_G_MEASURED);
4048 : }
4049 44 : pasRet[i].nCount =
4050 44 : static_cast<int64_t>(std::strtoll(pszCount, nullptr, 10));
4051 : }
4052 : }
4053 : }
4054 :
4055 26 : OGRPGClearResult(hResult);
4056 :
4057 26 : return pasRet;
4058 : }
4059 :
4060 : /************************************************************************/
4061 : /* FindFieldIndex() */
4062 : /************************************************************************/
4063 :
4064 24 : int OGRPGTableLayer::FindFieldIndex(const char *pszFieldName, int bExactMatch)
4065 : {
4066 24 : const auto poLayerDefn = GetLayerDefn();
4067 24 : int iField = poLayerDefn->GetFieldIndex(pszFieldName);
4068 :
4069 24 : if (!bExactMatch && iField < 0 && bLaunderColumnNames)
4070 : {
4071 10 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
4072 : char *pszSafeName =
4073 5 : OGRPGCommonLaunderName(pszFieldName, "PG", m_bUTF8ToASCII);
4074 5 : iField = poLayerDefn->GetFieldIndex(pszSafeName);
4075 5 : CPLFree(pszSafeName);
4076 : }
4077 :
4078 24 : return iField;
4079 : }
4080 :
4081 : #undef PQexec
|