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