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