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