Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRPGDataSource class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2000, Frank Warmerdam
9 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <string.h>
15 : #include "ogr_pg.h"
16 : #include "cpl_conv.h"
17 : #include "cpl_string.h"
18 : #include "cpl_hash_set.h"
19 : #include <cctype>
20 : #include <set>
21 :
22 : #define PQexec this_is_an_error
23 :
24 : static void OGRPGNoticeProcessor(void *arg, const char *pszMessage);
25 :
26 : /************************************************************************/
27 : /* OGRPGDataSource() */
28 : /************************************************************************/
29 :
30 : OGRPGDataSource::OGRPGDataSource() = default;
31 :
32 : /************************************************************************/
33 : /* ~OGRPGDataSource() */
34 : /************************************************************************/
35 :
36 838 : OGRPGDataSource::~OGRPGDataSource()
37 :
38 : {
39 419 : OGRPGDataSource::FlushCache(true);
40 :
41 419 : CPLFree(pszForcedTables);
42 419 : CSLDestroy(papszSchemaList);
43 :
44 885 : for (int i = 0; i < nLayers; i++)
45 466 : delete papoLayers[i];
46 :
47 419 : CPLFree(papoLayers);
48 :
49 419 : if (hPGConn != nullptr)
50 : {
51 : // If there are prelude statements, don't mess with transactions.
52 416 : if (CSLFetchNameValue(papszOpenOptions, "PRELUDE_STATEMENTS") ==
53 : nullptr)
54 408 : FlushSoftTransaction();
55 :
56 : /* --------------------------------------------------------------------
57 : */
58 : /* Send closing statements */
59 : /* --------------------------------------------------------------------
60 : */
61 : const char *pszClosingStatements =
62 416 : CSLFetchNameValue(papszOpenOptions, "CLOSING_STATEMENTS");
63 416 : if (pszClosingStatements != nullptr)
64 : {
65 : PGresult *hResult =
66 6 : OGRPG_PQexec(hPGConn, pszClosingStatements, TRUE);
67 6 : OGRPGClearResult(hResult);
68 : }
69 :
70 : /* XXX - mloskot: After the connection is closed, valgrind still
71 : * reports 36 bytes definitely lost, somewhere in the libpq.
72 : */
73 416 : PQfinish(hPGConn);
74 416 : hPGConn = nullptr;
75 : }
76 838 : }
77 :
78 : /************************************************************************/
79 : /* FlushCache() */
80 : /************************************************************************/
81 :
82 1750 : OGRErr OGRPGDataSource::FlushCacheWithRet(bool /* bAtClosing */)
83 : {
84 1750 : OGRErr eErr = EndCopy();
85 1750 : if (eErr == OGRERR_NONE)
86 : {
87 3153 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
88 : {
89 1404 : papoLayers[iLayer]->RunDeferredCreationIfNecessary();
90 : }
91 : }
92 1750 : return eErr;
93 : }
94 :
95 1694 : CPLErr OGRPGDataSource::FlushCache(bool bAtClosing)
96 : {
97 1694 : return FlushCacheWithRet(bAtClosing) == OGRERR_NONE ? CE_None : CE_Failure;
98 : }
99 :
100 : /************************************************************************/
101 : /* GetCurrentSchema() */
102 : /************************************************************************/
103 :
104 414 : CPLString OGRPGDataSource::GetCurrentSchema()
105 : {
106 : /* -------------------------------------------- */
107 : /* Get the current schema */
108 : /* -------------------------------------------- */
109 414 : PGresult *hResult = OGRPG_PQexec(hPGConn, "SELECT current_schema()");
110 414 : if (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0))
111 : {
112 414 : osCurrentSchema = PQgetvalue(hResult, 0, 0);
113 : }
114 414 : OGRPGClearResult(hResult);
115 :
116 828 : return osCurrentSchema;
117 : }
118 :
119 : /************************************************************************/
120 : /* OGRPGDecodeVersionString() */
121 : /************************************************************************/
122 :
123 646 : void OGRPGDataSource::OGRPGDecodeVersionString(PGver *psVersion,
124 : const char *pszVer)
125 : {
126 : // Skip leading spaces
127 646 : while (*pszVer == ' ')
128 0 : pszVer++;
129 1292 : std::string osVer(pszVer);
130 : // And truncate at the first space
131 646 : const auto nPosSpace = osVer.find(' ');
132 646 : if (nPosSpace != std::string::npos)
133 646 : osVer.resize(nPosSpace);
134 :
135 646 : memset(psVersion, 0, sizeof(*psVersion));
136 1292 : const CPLStringList aosTokens(CSLTokenizeString2(osVer.c_str(), ".", 0));
137 646 : if (aosTokens.size() >= 1)
138 646 : psVersion->nMajor = atoi(aosTokens[0]);
139 646 : if (aosTokens.size() >= 2)
140 646 : psVersion->nMinor = atoi(aosTokens[1]);
141 646 : if (aosTokens.size() >= 3)
142 0 : psVersion->nRelease = atoi(aosTokens[2]);
143 646 : }
144 :
145 : /************************************************************************/
146 : /* One entry for each PG table */
147 : /************************************************************************/
148 :
149 : struct PGTableEntry
150 : {
151 : char *pszTableName = nullptr;
152 : char *pszSchemaName = nullptr;
153 : char *pszDescription = nullptr;
154 : int nGeomColumnCount = 0;
155 : PGGeomColumnDesc *pasGeomColumns = nullptr; /* list of geometry columns */
156 : int bDerivedInfoAdded =
157 : false; /* set to TRUE if it derives from another table */
158 : };
159 :
160 1418 : static unsigned long OGRPGHashTableEntry(const void *_psTableEntry)
161 : {
162 1418 : const PGTableEntry *psTableEntry =
163 : static_cast<const PGTableEntry *>(_psTableEntry);
164 2836 : return CPLHashSetHashStr(CPLString().Printf(
165 2836 : "%s.%s", psTableEntry->pszSchemaName, psTableEntry->pszTableName));
166 : }
167 :
168 383 : static int OGRPGEqualTableEntry(const void *_psTableEntry1,
169 : const void *_psTableEntry2)
170 : {
171 383 : const PGTableEntry *psTableEntry1 =
172 : static_cast<const PGTableEntry *>(_psTableEntry1);
173 383 : const PGTableEntry *psTableEntry2 =
174 : static_cast<const PGTableEntry *>(_psTableEntry2);
175 383 : return strcmp(psTableEntry1->pszTableName, psTableEntry2->pszTableName) ==
176 760 : 0 &&
177 377 : strcmp(psTableEntry1->pszSchemaName, psTableEntry2->pszSchemaName) ==
178 383 : 0;
179 : }
180 :
181 156 : static void OGRPGTableEntryAddGeomColumn(
182 : PGTableEntry *psTableEntry, const char *pszName,
183 : const char *pszGeomType = nullptr, int GeometryTypeFlags = 0,
184 : int nSRID = UNDETERMINED_SRID, PostgisType ePostgisType = GEOM_TYPE_UNKNOWN,
185 : int bNullable = TRUE)
186 : {
187 312 : psTableEntry->pasGeomColumns = static_cast<PGGeomColumnDesc *>(CPLRealloc(
188 156 : psTableEntry->pasGeomColumns,
189 156 : sizeof(PGGeomColumnDesc) * (psTableEntry->nGeomColumnCount + 1)));
190 312 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].pszName =
191 156 : CPLStrdup(pszName);
192 156 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].pszGeomType =
193 156 : (pszGeomType) ? CPLStrdup(pszGeomType) : nullptr;
194 156 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount]
195 156 : .GeometryTypeFlags = GeometryTypeFlags;
196 : /* With PostGIS 2.0, querying geometry_columns can return 0, not only when
197 : */
198 : /* the SRID is truly set to 0, but also when there's no constraint */
199 156 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].nSRID =
200 156 : nSRID > 0 ? nSRID : UNDETERMINED_SRID;
201 156 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].ePostgisType =
202 : ePostgisType;
203 156 : psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].bNullable =
204 : bNullable;
205 156 : psTableEntry->nGeomColumnCount++;
206 156 : }
207 :
208 712 : static void OGRPGFreeTableEntry(void *_psTableEntry)
209 : {
210 712 : PGTableEntry *psTableEntry = static_cast<PGTableEntry *>(_psTableEntry);
211 712 : CPLFree(psTableEntry->pszTableName);
212 712 : CPLFree(psTableEntry->pszSchemaName);
213 712 : CPLFree(psTableEntry->pszDescription);
214 868 : for (int i = 0; i < psTableEntry->nGeomColumnCount; i++)
215 : {
216 156 : CPLFree(psTableEntry->pasGeomColumns[i].pszName);
217 156 : CPLFree(psTableEntry->pasGeomColumns[i].pszGeomType);
218 : }
219 712 : CPLFree(psTableEntry->pasGeomColumns);
220 712 : CPLFree(psTableEntry);
221 712 : }
222 :
223 360 : static PGTableEntry *OGRPGFindTableEntry(CPLHashSet *hSetTables,
224 : const char *pszTableName,
225 : const char *pszSchemaName)
226 : {
227 360 : PGTableEntry sEntry;
228 360 : sEntry.pszTableName = const_cast<char *>(pszTableName);
229 360 : sEntry.pszSchemaName = const_cast<char *>(pszSchemaName);
230 720 : return static_cast<PGTableEntry *>(CPLHashSetLookup(hSetTables, &sEntry));
231 : }
232 :
233 346 : static PGTableEntry *OGRPGAddTableEntry(CPLHashSet *hSetTables,
234 : const char *pszTableName,
235 : const char *pszSchemaName,
236 : const char *pszDescription)
237 : {
238 : PGTableEntry *psEntry =
239 346 : static_cast<PGTableEntry *>(CPLCalloc(1, sizeof(PGTableEntry)));
240 346 : psEntry->pszTableName = CPLStrdup(pszTableName);
241 346 : psEntry->pszSchemaName = CPLStrdup(pszSchemaName);
242 346 : psEntry->pszDescription = CPLStrdup(pszDescription ? pszDescription : "");
243 :
244 346 : CPLHashSetInsert(hSetTables, psEntry);
245 :
246 346 : return psEntry;
247 : }
248 :
249 : /************************************************************************/
250 : /* Open() */
251 : /************************************************************************/
252 :
253 419 : int OGRPGDataSource::Open(const char *pszNewName, int bUpdate, int bTestOpen,
254 : char **papszOpenOptionsIn)
255 :
256 : {
257 419 : CPLAssert(nLayers == 0);
258 419 : papszOpenOptions = CSLDuplicate(papszOpenOptionsIn);
259 :
260 : const char *pszPreludeStatements =
261 419 : CSLFetchNameValue(papszOpenOptions, "PRELUDE_STATEMENTS");
262 419 : if (pszPreludeStatements)
263 : {
264 : // If the prelude statements starts with BEGIN, then don't emit one
265 : // in our code.
266 8 : if (STARTS_WITH_CI(pszPreludeStatements, "BEGIN"))
267 6 : nSoftTransactionLevel = 1;
268 : }
269 :
270 : /* -------------------------------------------------------------------- */
271 : /* Verify postgresql prefix. */
272 : /* -------------------------------------------------------------------- */
273 419 : if (STARTS_WITH_CI(pszNewName, "PGB:"))
274 : {
275 : #if defined(BINARY_CURSOR_ENABLED)
276 : bUseBinaryCursor = TRUE;
277 : CPLDebug("PG", "BINARY cursor is used for geometry fetching");
278 : #endif
279 : }
280 419 : else if (!STARTS_WITH_CI(pszNewName, "PG:") &&
281 2 : !STARTS_WITH(pszNewName, "postgresql://"))
282 : {
283 0 : if (!bTestOpen)
284 0 : CPLError(CE_Failure, CPLE_AppDefined,
285 : "%s does not conform to PostgreSQL naming convention,"
286 : " PG:* or postgresql://\n",
287 : pszNewName);
288 0 : return FALSE;
289 : }
290 :
291 414 : const auto QuoteAndEscapeConnectionParam = [](const char *pszParam)
292 : {
293 414 : CPLString osRet("\'");
294 10764 : for (int i = 0; pszParam[i]; ++i)
295 : {
296 10350 : if (pszParam[i] == '\'')
297 0 : osRet += "\\'";
298 10350 : else if (pszParam[i] == '\\')
299 0 : osRet += "\\\\";
300 : else
301 10350 : osRet += pszParam[i];
302 : }
303 414 : osRet += '\'';
304 414 : return osRet;
305 : };
306 :
307 838 : CPLString osConnectionName(pszNewName);
308 419 : if (osConnectionName.find("PG:postgresql://") == 0)
309 1 : osConnectionName = osConnectionName.substr(3);
310 419 : const bool bIsURI = osConnectionName.find("postgresql://") == 0;
311 :
312 419 : const char *const apszOpenOptions[] = {
313 : "service", "dbname", "port", "user", "password", "host",
314 : // Non-postgreSQL options
315 : "active_schema", "schemas", "tables"};
316 838 : std::string osSchemas;
317 838 : std::string osForcedTables;
318 4190 : for (const char *pszOpenOption : apszOpenOptions)
319 : {
320 3771 : const char *pszVal = CSLFetchNameValue(papszOpenOptions, pszOpenOption);
321 3771 : if (pszVal && strcmp(pszOpenOption, "active_schema") == 0)
322 : {
323 1 : osActiveSchema = pszVal;
324 : }
325 3770 : else if (pszVal && strcmp(pszOpenOption, "schemas") == 0)
326 : {
327 0 : osSchemas = pszVal;
328 : }
329 3770 : else if (pszVal && strcmp(pszOpenOption, "tables") == 0)
330 : {
331 0 : osForcedTables = pszVal;
332 : }
333 3770 : else if (pszVal)
334 : {
335 2 : if (bIsURI)
336 : {
337 : osConnectionName +=
338 2 : osConnectionName.find('?') == std::string::npos ? '?' : '&';
339 : }
340 : else
341 : {
342 0 : if (osConnectionName.back() != ':')
343 0 : osConnectionName += ' ';
344 : }
345 2 : osConnectionName += pszOpenOption;
346 2 : osConnectionName += "=";
347 2 : if (bIsURI)
348 : {
349 2 : char *pszTmp = CPLEscapeString(pszVal, -1, CPLES_URL);
350 2 : osConnectionName += pszTmp;
351 2 : CPLFree(pszTmp);
352 : }
353 : else
354 : {
355 0 : osConnectionName += QuoteAndEscapeConnectionParam(pszVal);
356 : }
357 : }
358 : }
359 :
360 : /* -------------------------------------------------------------------- */
361 : /* Set application name if not found in connection string */
362 : /* -------------------------------------------------------------------- */
363 :
364 836 : if (strstr(pszNewName, "application_name") == nullptr &&
365 417 : getenv("PGAPPNAME") == nullptr)
366 : {
367 417 : if (bIsURI)
368 : {
369 : osConnectionName +=
370 3 : osConnectionName.find('?') == std::string::npos ? '?' : '&';
371 : }
372 : else
373 : {
374 414 : if (osConnectionName.back() != ':')
375 414 : osConnectionName += ' ';
376 : }
377 417 : osConnectionName += "application_name=";
378 834 : std::string osVal("GDAL ");
379 417 : osVal += GDALVersionInfo("RELEASE_NAME");
380 417 : if (bIsURI)
381 : {
382 3 : char *pszTmp = CPLEscapeString(osVal.c_str(), -1, CPLES_URL);
383 3 : osConnectionName += pszTmp;
384 3 : CPLFree(pszTmp);
385 : }
386 : else
387 : {
388 414 : osConnectionName += QuoteAndEscapeConnectionParam(osVal.c_str());
389 : }
390 : }
391 :
392 : const auto ParseAndRemoveParam =
393 1248 : [](char *pszStr, const char *pszParamName, std::string &osValue)
394 : {
395 1248 : const int nParamNameLen = static_cast<int>(strlen(pszParamName));
396 1248 : bool bInSingleQuotedString = false;
397 154001 : for (int i = 0; pszStr[i]; i++)
398 : {
399 153172 : if (bInSingleQuotedString)
400 : {
401 21722 : if (pszStr[i] == '\\')
402 : {
403 12 : if (pszStr[i + 1] == '\\' || pszStr[i + 1] == '\'')
404 : {
405 12 : ++i;
406 : }
407 : }
408 21710 : else if (pszStr[i] == '\'')
409 : {
410 841 : bInSingleQuotedString = false;
411 : }
412 : }
413 131450 : else if (pszStr[i] == '\'')
414 : {
415 841 : bInSingleQuotedString = true;
416 : }
417 130609 : else if (EQUALN(pszStr + i, pszParamName, nParamNameLen) &&
418 419 : (pszStr[i + nParamNameLen] == '=' ||
419 3 : pszStr[i + nParamNameLen] == ' '))
420 : {
421 419 : const int iStart = i;
422 419 : i += nParamNameLen;
423 423 : while (pszStr[i] == ' ')
424 4 : ++i;
425 419 : if (pszStr[i] == '=')
426 : {
427 419 : ++i;
428 425 : while (pszStr[i] == ' ')
429 6 : ++i;
430 419 : if (pszStr[i] == '\'')
431 : {
432 2 : ++i;
433 106 : for (; pszStr[i]; i++)
434 : {
435 106 : if (pszStr[i] == '\\')
436 : {
437 0 : if (pszStr[i + 1] == '\\' ||
438 0 : pszStr[i + 1] == '\'')
439 : {
440 0 : osValue += pszStr[i + 1];
441 0 : ++i;
442 : }
443 : }
444 106 : else if (pszStr[i] == '\'')
445 : {
446 2 : ++i;
447 2 : break;
448 : }
449 : else
450 : {
451 104 : osValue += pszStr[i];
452 : }
453 : }
454 : }
455 : else
456 : {
457 11962 : for (; pszStr[i] && pszStr[i] != ' '; i++)
458 : {
459 11545 : osValue += pszStr[i];
460 : }
461 : }
462 :
463 : // Edit pszStr to remove the parameter and its value
464 419 : if (pszStr[i] == ' ')
465 : {
466 417 : memmove(pszStr + iStart, pszStr + i,
467 417 : strlen(pszStr + i) + 1);
468 : }
469 : else
470 : {
471 2 : pszStr[iStart] = 0;
472 : }
473 : }
474 419 : return true;
475 : }
476 : }
477 829 : return false;
478 : };
479 :
480 419 : char *pszConnectionName = CPLStrdup(osConnectionName);
481 419 : char *pszConnectionNameNoPrefix =
482 838 : pszConnectionName + (STARTS_WITH_CI(pszConnectionName, "PGB:") ? 4
483 419 : : STARTS_WITH_CI(pszConnectionName, "PG:") ? 3
484 : : 0);
485 :
486 : /* -------------------------------------------------------------------- */
487 : /* Determine if the connection string contains an optional */
488 : /* ACTIVE_SCHEMA portion. If so, parse it out. */
489 : /* -------------------------------------------------------------------- */
490 835 : if (osActiveSchema.empty() && !bIsURI &&
491 416 : !ParseAndRemoveParam(pszConnectionNameNoPrefix, "active_schema",
492 : osActiveSchema))
493 : {
494 412 : osActiveSchema = "public";
495 : }
496 :
497 : /* -------------------------------------------------------------------- */
498 : /* Determine if the connection string contains an optional */
499 : /* SCHEMAS portion. If so, parse it out. */
500 : /* -------------------------------------------------------------------- */
501 838 : if (!osSchemas.empty() ||
502 419 : (!bIsURI &&
503 416 : ParseAndRemoveParam(pszConnectionNameNoPrefix, "schemas", osSchemas)))
504 : {
505 409 : papszSchemaList = CSLTokenizeString2(osSchemas.c_str(), ",", 0);
506 :
507 : /* If there is only one schema specified, make it the active schema */
508 409 : if (CSLCount(papszSchemaList) == 1)
509 : {
510 407 : osActiveSchema = papszSchemaList[0];
511 : }
512 : }
513 :
514 : /* -------------------------------------------------------------------- */
515 : /* Determine if the connection string contains an optional */
516 : /* TABLES portion. If so, parse it out. The expected */
517 : /* connection string in this case will be, e.g.: */
518 : /* */
519 : /* 'PG:dbname=warmerda user=warmerda tables=s1.t1,[s2.t2,...] */
520 : /* - where sN is schema and tN is table name */
521 : /* We must also strip this information from the connection */
522 : /* string; PQconnectdb() does not like unknown directives */
523 : /* -------------------------------------------------------------------- */
524 838 : if (!osForcedTables.empty() ||
525 419 : (!bIsURI && ParseAndRemoveParam(pszConnectionNameNoPrefix, "tables",
526 : osForcedTables)))
527 : {
528 6 : pszForcedTables = CPLStrdup(osForcedTables.c_str());
529 : }
530 :
531 : /* -------------------------------------------------------------------- */
532 : /* Try to establish connection. */
533 : /* -------------------------------------------------------------------- */
534 419 : hPGConn = PQconnectdb(pszConnectionNameNoPrefix);
535 419 : CPLFree(pszConnectionName);
536 419 : pszConnectionName = nullptr;
537 :
538 419 : if (hPGConn == nullptr || PQstatus(hPGConn) == CONNECTION_BAD)
539 : {
540 3 : CPLError(CE_Failure, CPLE_AppDefined, "PQconnectdb failed.\n%s",
541 3 : PQerrorMessage(hPGConn));
542 :
543 3 : PQfinish(hPGConn);
544 3 : hPGConn = nullptr;
545 :
546 3 : return FALSE;
547 : }
548 :
549 416 : bDSUpdate = bUpdate;
550 :
551 : /* -------------------------------------------------------------------- */
552 : /* Send prelude statements */
553 : /* -------------------------------------------------------------------- */
554 416 : if (pszPreludeStatements != nullptr)
555 : {
556 8 : PGresult *hResult = OGRPG_PQexec(hPGConn, pszPreludeStatements, TRUE);
557 8 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
558 : {
559 2 : OGRPGClearResult(hResult);
560 2 : return FALSE;
561 : }
562 :
563 6 : OGRPGClearResult(hResult);
564 : }
565 :
566 : /* -------------------------------------------------------------------- */
567 : /* Set the encoding to UTF8 as the driver advertises UTF8 */
568 : /* unless PGCLIENTENCODING is defined */
569 : /* -------------------------------------------------------------------- */
570 414 : if (CPLGetConfigOption("PGCLIENTENCODING", nullptr) == nullptr)
571 : {
572 412 : const char *encoding = "UTF8";
573 412 : if (PQsetClientEncoding(hPGConn, encoding) == -1)
574 : {
575 0 : CPLError(CE_Warning, CPLE_AppDefined,
576 : "PQsetClientEncoding(%s) failed.\n%s", encoding,
577 0 : PQerrorMessage(hPGConn));
578 : }
579 : }
580 :
581 : {
582 414 : PGresult *hResult = OGRPG_PQexec(hPGConn, "SHOW client_encoding");
583 828 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
584 414 : PQntuples(hResult) == 1)
585 : {
586 414 : const char *pszClientEncoding = PQgetvalue(hResult, 0, 0);
587 414 : if (pszClientEncoding)
588 : {
589 414 : CPLDebug("PG", "Client encoding: '%s'", pszClientEncoding);
590 414 : if (EQUAL(pszClientEncoding, "UTF8"))
591 : {
592 412 : m_bUTF8ClientEncoding = true;
593 : }
594 : }
595 : }
596 414 : OGRPGClearResult(hResult);
597 : }
598 :
599 : /* -------------------------------------------------------------------- */
600 : /* Install a notice processor. */
601 : /* -------------------------------------------------------------------- */
602 414 : PQsetNoticeProcessor(hPGConn, OGRPGNoticeProcessor, this);
603 :
604 : /* -------------------------------------------------------------------- */
605 : /* Detect PostGIS schema */
606 : /* -------------------------------------------------------------------- */
607 828 : CPLString osPostgisSchema;
608 : {
609 414 : PGresult *hResult = OGRPG_PQexec(
610 : hPGConn,
611 : "SELECT n.nspname FROM pg_proc p JOIN pg_namespace n "
612 414 : "ON n.oid = p.pronamespace WHERE proname = 'postgis_version'");
613 828 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
614 414 : PQntuples(hResult) > 0)
615 : {
616 414 : const char *pszPostgisSchema = PQgetvalue(hResult, 0, 0);
617 :
618 414 : CPLDebug("PG", "PostGIS schema: '%s'", pszPostgisSchema);
619 :
620 414 : osPostgisSchema = pszPostgisSchema;
621 : }
622 414 : OGRPGClearResult(hResult);
623 : }
624 :
625 : /* -------------------------------------------------------------------- */
626 : /* Get search_path */
627 : /* -------------------------------------------------------------------- */
628 828 : std::string osSearchPath = "public";
629 : {
630 414 : PGresult *hResult = OGRPG_PQexec(hPGConn, "SHOW search_path");
631 828 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
632 414 : PQntuples(hResult) > 0)
633 : {
634 414 : const char *pszVal = PQgetvalue(hResult, 0, 0);
635 414 : if (pszVal)
636 : {
637 414 : osSearchPath = pszVal;
638 : }
639 : }
640 414 : OGRPGClearResult(hResult);
641 : }
642 :
643 : /* -------------------------------------------------------------------- */
644 : /* Set active schema if different from 'public'. Also add */
645 : /* postgis schema if needed. */
646 : /* -------------------------------------------------------------------- */
647 417 : if (osActiveSchema != "public" ||
648 6 : (!osPostgisSchema.empty() &&
649 3 : osSearchPath.find(osPostgisSchema) == std::string::npos))
650 : {
651 411 : std::string osNewSearchPath;
652 411 : if (osActiveSchema != "public")
653 : {
654 : osNewSearchPath +=
655 411 : OGRPGEscapeString(hPGConn, osActiveSchema.c_str());
656 : }
657 411 : if (!osSearchPath.empty() && osSearchPath != "\"\"")
658 : {
659 410 : if (!osNewSearchPath.empty())
660 410 : osNewSearchPath += ',';
661 410 : osNewSearchPath += osSearchPath;
662 : }
663 822 : if (!osPostgisSchema.empty() &&
664 411 : osSearchPath.find(osPostgisSchema) == std::string::npos)
665 : {
666 1 : if (!osNewSearchPath.empty())
667 1 : osNewSearchPath += ',';
668 : osNewSearchPath +=
669 1 : OGRPGEscapeString(hPGConn, osPostgisSchema.c_str());
670 : }
671 411 : CPLDebug("PG", "Modifying search_path from %s to %s",
672 : osSearchPath.c_str(), osNewSearchPath.c_str());
673 :
674 411 : std::string osCommand = "SET search_path=" + osNewSearchPath;
675 411 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
676 :
677 411 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
678 : {
679 0 : OGRPGClearResult(hResult);
680 0 : CPLDebug("PG", "Command \"%s\" failed. Trying without 'public'.",
681 : osCommand.c_str());
682 : osCommand = CPLSPrintf(
683 : "SET search_path=%s",
684 0 : OGRPGEscapeString(hPGConn, osActiveSchema.c_str()).c_str());
685 0 : PGresult *hResult2 = OGRPG_PQexec(hPGConn, osCommand.c_str());
686 :
687 0 : if (!hResult2 || PQresultStatus(hResult2) != PGRES_COMMAND_OK)
688 : {
689 0 : OGRPGClearResult(hResult2);
690 :
691 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
692 0 : PQerrorMessage(hPGConn));
693 :
694 0 : return FALSE;
695 : }
696 : }
697 :
698 411 : OGRPGClearResult(hResult);
699 : }
700 :
701 : /* -------------------------------------------------------------------- */
702 : /* Find out PostgreSQL version */
703 : /* -------------------------------------------------------------------- */
704 414 : sPostgreSQLVersion.nMajor = -1;
705 414 : sPostgreSQLVersion.nMinor = -1;
706 414 : sPostgreSQLVersion.nRelease = -1;
707 :
708 414 : PGresult *hResult = OGRPG_PQexec(hPGConn, "SELECT version()");
709 828 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
710 414 : PQntuples(hResult) > 0)
711 : {
712 414 : char *pszVer = PQgetvalue(hResult, 0, 0);
713 :
714 414 : CPLDebug("PG", "PostgreSQL version string : '%s'", pszVer);
715 :
716 : /* Should work with "PostgreSQL X.Y.Z ..." or "EnterpriseDB X.Y.Z ..."
717 : */
718 414 : const char *pszSpace = strchr(pszVer, ' ');
719 414 : if (pszSpace != nullptr &&
720 414 : isdigit(static_cast<unsigned char>(pszSpace[1])))
721 : {
722 414 : OGRPGDecodeVersionString(&sPostgreSQLVersion, pszSpace + 1);
723 : }
724 : }
725 414 : OGRPGClearResult(hResult);
726 414 : CPLAssert(nullptr ==
727 : hResult); /* Test if safe PQclear has not been broken */
728 :
729 : /* -------------------------------------------------------------------- */
730 : /* Set standard_conforming_strings=ON */
731 : /* -------------------------------------------------------------------- */
732 :
733 414 : hResult = OGRPG_PQexec(hPGConn, "SET standard_conforming_strings = ON");
734 414 : if (!(hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK))
735 : {
736 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
737 0 : OGRPGClearResult(hResult);
738 0 : return FALSE;
739 : }
740 414 : OGRPGClearResult(hResult);
741 :
742 : /* -------------------------------------------------------------------- */
743 : /* Test if time binary format is int8 or float8 */
744 : /* -------------------------------------------------------------------- */
745 : #if defined(BINARY_CURSOR_ENABLED)
746 : if (bUseBinaryCursor)
747 : {
748 : SoftStartTransaction();
749 :
750 : hResult =
751 : OGRPG_PQexec(hPGConn, "DECLARE gettimebinaryformat BINARY CURSOR "
752 : "FOR SELECT CAST ('00:00:01' AS time)");
753 :
754 : if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
755 : {
756 : OGRPGClearResult(hResult);
757 :
758 : hResult = OGRPG_PQexec(hPGConn, "FETCH ALL IN gettimebinaryformat");
759 :
760 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
761 : PQntuples(hResult) == 1)
762 : {
763 : if (PQfformat(hResult, 0) == 1) // Binary data representation
764 : {
765 : CPLAssert(PQgetlength(hResult, 0, 0) == 8);
766 : unsigned int nVal[2] = {0, 0};
767 : memcpy(nVal, PQgetvalue(hResult, 0, 0), 8);
768 : CPL_MSBPTR32(&nVal[0]);
769 : CPL_MSBPTR32(&nVal[1]);
770 : double dVal = 0.0;
771 : memcpy(&dVal, PQgetvalue(hResult, 0, 0), 8);
772 : CPL_MSBPTR64(&dVal);
773 : if (nVal[0] == 0 && nVal[1] == 1000000)
774 : {
775 : bBinaryTimeFormatIsInt8 = TRUE;
776 : CPLDebug("PG", "Time binary format is int8");
777 : }
778 : else if (dVal == 1.)
779 : {
780 : bBinaryTimeFormatIsInt8 = FALSE;
781 : CPLDebug("PG", "Time binary format is float8");
782 : }
783 : else
784 : {
785 : bBinaryTimeFormatIsInt8 = FALSE;
786 : CPLDebug("PG", "Time binary format is unknown");
787 : }
788 : }
789 : }
790 : }
791 :
792 : OGRPGClearResult(hResult);
793 :
794 : hResult = OGRPG_PQexec(hPGConn, "CLOSE gettimebinaryformat");
795 : OGRPGClearResult(hResult);
796 :
797 : SoftCommitTransaction();
798 : }
799 : #endif
800 :
801 : /* -------------------------------------------------------------------- */
802 : /* Test to see if this database instance has support for the */
803 : /* PostGIS Geometry type. If so, disable sequential scanning */
804 : /* so we will get the value of the gist indexes. */
805 : /* -------------------------------------------------------------------- */
806 414 : hResult =
807 414 : OGRPG_PQexec(hPGConn, "SELECT oid, typname FROM pg_type WHERE typname "
808 : "IN ('geometry', 'geography') AND typtype='b'");
809 :
810 414 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
811 1242 : PQntuples(hResult) > 0 &&
812 414 : CPLTestBool(CPLGetConfigOption("PG_USE_POSTGIS", "YES")))
813 : {
814 696 : for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
815 : {
816 464 : const char *pszOid = PQgetvalue(hResult, iRecord, 0);
817 464 : const char *pszTypname = PQgetvalue(hResult, iRecord, 1);
818 464 : if (EQUAL(pszTypname, "geometry"))
819 : {
820 232 : bHavePostGIS = TRUE;
821 232 : nGeometryOID = static_cast<Oid>(strtoul(pszOid, nullptr, 10));
822 : }
823 232 : else if (CPLTestBool(CPLGetConfigOption("PG_USE_GEOGRAPHY", "YES")))
824 : {
825 232 : bHaveGeography = TRUE;
826 232 : nGeographyOID = static_cast<Oid>(strtoul(pszOid, nullptr, 10));
827 : }
828 : }
829 : }
830 :
831 414 : OGRPGClearResult(hResult);
832 :
833 : /* -------------------------------------------------------------------- */
834 : /* Find out PostGIS version */
835 : /* -------------------------------------------------------------------- */
836 :
837 414 : sPostGISVersion.nMajor = -1;
838 414 : sPostGISVersion.nMinor = -1;
839 414 : sPostGISVersion.nRelease = -1;
840 :
841 414 : if (bHavePostGIS)
842 : {
843 232 : hResult = OGRPG_PQexec(hPGConn, "SELECT postgis_version()");
844 464 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
845 232 : PQntuples(hResult) > 0)
846 : {
847 232 : char *pszVer = PQgetvalue(hResult, 0, 0);
848 :
849 232 : CPLDebug("PG", "PostGIS version string : '%s'", pszVer);
850 :
851 232 : OGRPGDecodeVersionString(&sPostGISVersion, pszVer);
852 : }
853 232 : OGRPGClearResult(hResult);
854 : }
855 :
856 414 : m_bHasGeometryColumns =
857 414 : OGRPG_Check_Table_Exists(hPGConn, "geometry_columns");
858 414 : m_bHasSpatialRefSys = OGRPG_Check_Table_Exists(hPGConn, "spatial_ref_sys");
859 :
860 : /* -------------------------------------------------------------------- */
861 : /* Find out "unknown SRID" value */
862 : /* -------------------------------------------------------------------- */
863 :
864 414 : if (sPostGISVersion.nMajor >= 2)
865 : {
866 232 : hResult =
867 232 : OGRPG_PQexec(hPGConn, "SELECT ST_Srid('POINT EMPTY'::GEOMETRY)");
868 :
869 464 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
870 232 : PQntuples(hResult) > 0)
871 : {
872 232 : nUndefinedSRID = atoi(PQgetvalue(hResult, 0, 0));
873 : }
874 :
875 232 : OGRPGClearResult(hResult);
876 : }
877 : else
878 182 : nUndefinedSRID = -1;
879 :
880 414 : GetCurrentSchema();
881 :
882 828 : bListAllTables = CPLTestBool(
883 414 : CSLFetchNameValueDef(papszOpenOptions, "LIST_ALL_TABLES",
884 : CPLGetConfigOption("PG_LIST_ALL_TABLES", "NO")));
885 :
886 828 : m_bSkipViews = CPLTestBool(
887 414 : CSLFetchNameValueDef(papszOpenOptions, "SKIP_VIEWS",
888 : CPLGetConfigOption("PG_SKIP_VIEWS", "NO")));
889 :
890 414 : return TRUE;
891 : }
892 :
893 : /************************************************************************/
894 : /* LoadTables() */
895 : /************************************************************************/
896 :
897 1243 : void OGRPGDataSource::LoadTables()
898 : {
899 1243 : if (bHasLoadTables)
900 970 : return;
901 273 : bHasLoadTables = TRUE;
902 :
903 273 : PGTableEntry **papsTables = nullptr;
904 273 : int nTableCount = 0;
905 273 : CPLHashSet *hSetTables = nullptr;
906 546 : std::set<CPLString> osRegisteredLayers;
907 :
908 307 : for (int i = 0; i < nLayers; i++)
909 : {
910 34 : osRegisteredLayers.insert(papoLayers[i]->GetName());
911 : }
912 :
913 273 : if (pszForcedTables)
914 : {
915 4 : char **papszTableList = CSLTokenizeString2(pszForcedTables, ",", 0);
916 :
917 10 : for (int i = 0; i < CSLCount(papszTableList); i++)
918 : {
919 : // Get schema and table name
920 : char **papszQualifiedParts =
921 6 : CSLTokenizeString2(papszTableList[i], ".", 0);
922 6 : int nParts = CSLCount(papszQualifiedParts);
923 :
924 6 : if (nParts == 1 || nParts == 2)
925 : {
926 : /* Find the geometry column name if specified */
927 6 : char *pszGeomColumnName = nullptr;
928 6 : char *pos = strchr(
929 6 : papszQualifiedParts[CSLCount(papszQualifiedParts) - 1],
930 : '(');
931 6 : if (pos != nullptr)
932 : {
933 0 : *pos = '\0';
934 0 : pszGeomColumnName = pos + 1;
935 0 : int len = static_cast<int>(strlen(pszGeomColumnName));
936 0 : if (len > 0)
937 0 : pszGeomColumnName[len - 1] = '\0';
938 : }
939 :
940 12 : papsTables = static_cast<PGTableEntry **>(CPLRealloc(
941 6 : papsTables, sizeof(PGTableEntry *) * (nTableCount + 1)));
942 12 : papsTables[nTableCount] = static_cast<PGTableEntry *>(
943 6 : CPLCalloc(1, sizeof(PGTableEntry)));
944 6 : if (pszGeomColumnName)
945 0 : OGRPGTableEntryAddGeomColumn(papsTables[nTableCount],
946 : pszGeomColumnName);
947 :
948 6 : if (nParts == 2)
949 : {
950 0 : papsTables[nTableCount]->pszSchemaName =
951 0 : CPLStrdup(papszQualifiedParts[0]);
952 0 : papsTables[nTableCount]->pszTableName =
953 0 : CPLStrdup(papszQualifiedParts[1]);
954 : }
955 : else
956 : {
957 12 : papsTables[nTableCount]->pszSchemaName =
958 6 : CPLStrdup(osActiveSchema.c_str());
959 12 : papsTables[nTableCount]->pszTableName =
960 6 : CPLStrdup(papszQualifiedParts[0]);
961 : }
962 6 : nTableCount++;
963 : }
964 :
965 6 : CSLDestroy(papszQualifiedParts);
966 : }
967 :
968 4 : CSLDestroy(papszTableList);
969 : }
970 :
971 : /* -------------------------------------------------------------------- */
972 : /* Get a list of available tables if they have not been */
973 : /* specified through the TABLES connection string param */
974 : /* -------------------------------------------------------------------- */
975 273 : const char *pszAllowedRelations = m_bSkipViews ? "'r'" : "'r','v','m','f'";
976 :
977 273 : hSetTables = CPLHashSetNew(OGRPGHashTableEntry, OGRPGEqualTableEntry,
978 : OGRPGFreeTableEntry);
979 :
980 269 : if (nTableCount == 0 && bHavePostGIS && sPostGISVersion.nMajor >= 2 &&
981 693 : !bListAllTables &&
982 : /* Config option mostly for comparison/debugging/etc... */
983 151 : CPLTestBool(CPLGetConfigOption("PG_USE_POSTGIS2_OPTIM", "YES")))
984 : {
985 : /* --------------------------------------------------------------------
986 : */
987 : /* With PostGIS 2.0, the geometry_columns and geography_columns */
988 : /* are views, based on the catalog system, that can be slow to */
989 : /* query, so query directly the catalog system. */
990 : /* See http://trac.osgeo.org/postgis/ticket/3092 */
991 : /* --------------------------------------------------------------------
992 : */
993 150 : CPLString osCommand;
994 300 : const char *pszConstraintDef = sPostgreSQLVersion.nMajor >= 12
995 150 : ? "pg_get_constraintdef(s.oid)"
996 : : "s.consrc";
997 : osCommand.Printf(
998 : "SELECT c.relname, n.nspname, c.relkind, a.attname, t.typname, "
999 : "postgis_typmod_dims(a.atttypmod) dim, "
1000 : "postgis_typmod_srid(a.atttypmod) srid, "
1001 : "postgis_typmod_type(a.atttypmod)::text geomtyp, "
1002 : "array_agg(%s)::text att_constraints, a.attnotnull, "
1003 : "d.description "
1004 : "FROM pg_class c JOIN pg_attribute a ON a.attrelid=c.oid "
1005 : "JOIN pg_namespace n ON c.relnamespace = n.oid "
1006 : "AND c.relkind in (%s) AND NOT ( n.nspname = 'public' AND "
1007 : "c.relname = 'raster_columns' ) "
1008 : "JOIN pg_type t ON a.atttypid = t.oid AND (t.typname = "
1009 : "'geometry'::name OR t.typname = 'geography'::name) "
1010 : "LEFT JOIN pg_constraint s ON s.connamespace = n.oid AND "
1011 : "s.conrelid = c.oid "
1012 : "AND a.attnum = ANY (s.conkey) "
1013 : "AND (%s LIKE '%%geometrytype(%% = %%' OR %s LIKE '%%ndims(%% = "
1014 : "%%' OR %s LIKE '%%srid(%% = %%') "
1015 : "LEFT JOIN pg_description d ON d.objoid = c.oid AND d.classoid = "
1016 : "'pg_class'::regclass::oid AND d.objsubid = 0 "
1017 : "GROUP BY c.relname, n.nspname, c.relkind, a.attname, t.typname, "
1018 : "dim, srid, geomtyp, a.attnotnull, c.oid, a.attnum, d.description "
1019 : "ORDER BY c.oid, a.attnum",
1020 : pszConstraintDef, pszAllowedRelations, pszConstraintDef,
1021 150 : pszConstraintDef, pszConstraintDef);
1022 150 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1023 :
1024 150 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
1025 : {
1026 0 : OGRPGClearResult(hResult);
1027 :
1028 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
1029 0 : PQerrorMessage(hPGConn));
1030 0 : goto end;
1031 : }
1032 : /* --------------------------------------------------------------------
1033 : */
1034 : /* Parse the returned table list */
1035 : /* --------------------------------------------------------------------
1036 : */
1037 226 : for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
1038 : {
1039 76 : const char *pszTable = PQgetvalue(hResult, iRecord, 0);
1040 76 : const char *pszSchemaName = PQgetvalue(hResult, iRecord, 1);
1041 76 : const char *pszGeomColumnName = PQgetvalue(hResult, iRecord, 3);
1042 76 : const char *pszGeomOrGeography = PQgetvalue(hResult, iRecord, 4);
1043 76 : const char *pszDim = PQgetvalue(hResult, iRecord, 5);
1044 76 : const char *pszSRID = PQgetvalue(hResult, iRecord, 6);
1045 76 : const char *pszGeomType = PQgetvalue(hResult, iRecord, 7);
1046 76 : const char *pszConstraint = PQgetvalue(hResult, iRecord, 8);
1047 76 : const char *pszNotNull = PQgetvalue(hResult, iRecord, 9);
1048 76 : const char *pszDescription = PQgetvalue(hResult, iRecord, 10);
1049 : /*const char *pszRelkind = PQgetvalue(hResult, iRecord, 2);
1050 : CPLDebug("PG", "%s %s %s %s %s %s %s %s %s %s",
1051 : pszTable, pszSchemaName, pszRelkind,
1052 : pszGeomColumnName, pszGeomOrGeography, pszDim,
1053 : pszSRID, pszGeomType, pszConstraint, pszNotNull);*/
1054 :
1055 76 : int bNullable = EQUAL(pszNotNull, "f");
1056 :
1057 76 : PostgisType ePostgisType = GEOM_TYPE_UNKNOWN;
1058 76 : if (EQUAL(pszGeomOrGeography, "geometry"))
1059 71 : ePostgisType = GEOM_TYPE_GEOMETRY;
1060 5 : else if (EQUAL(pszGeomOrGeography, "geography"))
1061 5 : ePostgisType = GEOM_TYPE_GEOGRAPHY;
1062 :
1063 76 : int nGeomCoordDimension = atoi(pszDim);
1064 76 : bool bHasM = pszGeomType[strlen(pszGeomType) - 1] == 'M';
1065 76 : int nSRID = atoi(pszSRID);
1066 :
1067 : /* Analyze constraints that might override geometrytype, */
1068 : /* coordinate dimension and SRID */
1069 152 : CPLString osConstraint(pszConstraint);
1070 76 : osConstraint = osConstraint.tolower();
1071 76 : pszConstraint = osConstraint.c_str();
1072 76 : const char *pszNeedle = strstr(pszConstraint, "geometrytype(");
1073 152 : CPLString osGeometryType;
1074 76 : if (pszNeedle)
1075 : {
1076 4 : pszNeedle = strchr(pszNeedle, '\'');
1077 4 : if (pszNeedle)
1078 : {
1079 4 : pszNeedle++;
1080 4 : const char *pszEnd = strchr(pszNeedle, '\'');
1081 4 : if (pszEnd)
1082 : {
1083 4 : osGeometryType = pszNeedle;
1084 4 : osGeometryType.resize(pszEnd - pszNeedle);
1085 4 : pszGeomType = osGeometryType.c_str();
1086 4 : bHasM = pszGeomType[strlen(pszGeomType) - 1] == 'M';
1087 : }
1088 : }
1089 : }
1090 :
1091 76 : pszNeedle = strstr(pszConstraint, "srid(");
1092 76 : if (pszNeedle)
1093 : {
1094 4 : pszNeedle = strchr(pszNeedle, '=');
1095 4 : if (pszNeedle)
1096 : {
1097 4 : pszNeedle++;
1098 4 : nSRID = atoi(pszNeedle);
1099 : }
1100 : }
1101 :
1102 76 : pszNeedle = strstr(pszConstraint, "ndims(");
1103 76 : if (pszNeedle)
1104 : {
1105 4 : pszNeedle = strchr(pszNeedle, '=');
1106 4 : if (pszNeedle)
1107 : {
1108 4 : pszNeedle++;
1109 4 : nGeomCoordDimension = atoi(pszNeedle);
1110 : }
1111 : }
1112 :
1113 76 : int GeomTypeFlags = 0;
1114 76 : if (nGeomCoordDimension == 3)
1115 : {
1116 26 : if (bHasM)
1117 0 : GeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
1118 : else
1119 26 : GeomTypeFlags |= OGRGeometry::OGR_G_3D;
1120 : }
1121 50 : else if (nGeomCoordDimension == 4)
1122 : {
1123 3 : GeomTypeFlags |=
1124 : OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
1125 : }
1126 :
1127 152 : papsTables = static_cast<PGTableEntry **>(CPLRealloc(
1128 76 : papsTables, sizeof(PGTableEntry *) * (nTableCount + 1)));
1129 152 : papsTables[nTableCount] =
1130 76 : static_cast<PGTableEntry *>(CPLCalloc(1, sizeof(PGTableEntry)));
1131 76 : papsTables[nTableCount]->pszTableName = CPLStrdup(pszTable);
1132 76 : papsTables[nTableCount]->pszSchemaName = CPLStrdup(pszSchemaName);
1133 152 : papsTables[nTableCount]->pszDescription =
1134 76 : CPLStrdup(pszDescription ? pszDescription : "");
1135 :
1136 76 : OGRPGTableEntryAddGeomColumn(
1137 76 : papsTables[nTableCount], pszGeomColumnName, pszGeomType,
1138 : GeomTypeFlags, nSRID, ePostgisType, bNullable);
1139 76 : nTableCount++;
1140 :
1141 : PGTableEntry *psEntry =
1142 76 : OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName);
1143 76 : if (psEntry == nullptr)
1144 62 : psEntry = OGRPGAddTableEntry(hSetTables, pszTable,
1145 : pszSchemaName, pszDescription);
1146 76 : OGRPGTableEntryAddGeomColumn(psEntry, pszGeomColumnName,
1147 : pszGeomType, GeomTypeFlags, nSRID,
1148 : ePostgisType, bNullable);
1149 : }
1150 :
1151 150 : OGRPGClearResult(hResult);
1152 : }
1153 123 : else if (nTableCount == 0)
1154 : {
1155 119 : CPLString osCommand;
1156 :
1157 : /* Caution : in PostGIS case, the result has 11 columns, whereas in the
1158 : */
1159 : /* non-PostGIS case it has only 3 columns */
1160 119 : if (bHavePostGIS && !bListAllTables)
1161 : {
1162 : osCommand.Printf(
1163 : "SELECT c.relname, n.nspname, g.f_geometry_column, "
1164 : "g.type, g.coord_dimension, g.srid, %d, a.attnotnull, "
1165 : "d.description, c.oid as oid, a.attnum as attnum "
1166 : "FROM pg_class c "
1167 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
1168 : "JOIN geometry_columns g "
1169 : "ON c.relname::TEXT = g.f_table_name::TEXT AND n.nspname = "
1170 : "g.f_table_schema "
1171 : "JOIN pg_attribute a "
1172 : "ON a.attname = g.f_geometry_column AND a.attrelid = c.oid "
1173 : "LEFT JOIN pg_description d "
1174 : "ON d.objoid = c.oid AND d.classoid = "
1175 : "'pg_class'::regclass::oid AND d.objsubid = 0 "
1176 : "WHERE c.relkind in (%s) ",
1177 1 : GEOM_TYPE_GEOMETRY, pszAllowedRelations);
1178 :
1179 1 : if (bHaveGeography)
1180 2 : osCommand += CPLString().Printf(
1181 : "UNION SELECT c.relname, n.nspname, "
1182 : "g.f_geography_column, "
1183 : "g.type, g.coord_dimension, g.srid, %d, a.attnotnull, "
1184 : "d.description, c.oid as oid, a.attnum as attnum "
1185 : "FROM pg_class c "
1186 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
1187 : "JOIN geography_columns g "
1188 : "ON c.relname::TEXT = g.f_table_name::TEXT AND n.nspname = "
1189 : "g.f_table_schema "
1190 : "JOIN pg_attribute a "
1191 : "ON a.attname = g.f_geography_column AND a.attrelid = "
1192 : "c.oid "
1193 : "LEFT JOIN pg_description d "
1194 : "ON d.objoid = c.oid AND d.classoid = "
1195 : "'pg_class'::regclass::oid AND d.objsubid = 0 "
1196 : "WHERE c.relkind in (%s)",
1197 1 : GEOM_TYPE_GEOGRAPHY, pszAllowedRelations);
1198 1 : osCommand += " ORDER BY oid, attnum";
1199 : }
1200 : else
1201 : osCommand.Printf("SELECT c.relname, n.nspname, d.description FROM "
1202 : "pg_class c "
1203 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
1204 : "LEFT JOIN pg_description d "
1205 : "ON d.objoid = c.oid AND d.classoid = "
1206 : "'pg_class'::regclass::oid AND d.objsubid = 0 "
1207 : "WHERE (c.relkind in (%s) AND "
1208 : "c.relname !~ '^pg_')",
1209 118 : pszAllowedRelations);
1210 :
1211 119 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1212 :
1213 119 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
1214 : {
1215 0 : OGRPGClearResult(hResult);
1216 :
1217 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
1218 0 : PQerrorMessage(hPGConn));
1219 0 : goto end;
1220 : }
1221 :
1222 : /* --------------------------------------------------------------------
1223 : */
1224 : /* Parse the returned table list */
1225 : /* --------------------------------------------------------------------
1226 : */
1227 8410 : for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
1228 : {
1229 8291 : const char *pszTable = PQgetvalue(hResult, iRecord, 0);
1230 8291 : const char *pszSchemaName = PQgetvalue(hResult, iRecord, 1);
1231 8291 : const char *pszGeomColumnName = nullptr;
1232 8291 : const char *pszGeomType = nullptr;
1233 8291 : const char *pszDescription = nullptr;
1234 8291 : int nGeomCoordDimension = 0;
1235 8291 : bool bHasM = false;
1236 8291 : int nSRID = 0;
1237 8291 : int bNullable = TRUE;
1238 8291 : PostgisType ePostgisType = GEOM_TYPE_UNKNOWN;
1239 8291 : if (bHavePostGIS && !bListAllTables)
1240 : {
1241 2 : pszGeomColumnName = PQgetvalue(hResult, iRecord, 2);
1242 2 : pszGeomType = PQgetvalue(hResult, iRecord, 3);
1243 2 : bHasM = pszGeomType[strlen(pszGeomType) - 1] == 'M';
1244 2 : nGeomCoordDimension = atoi(PQgetvalue(hResult, iRecord, 4));
1245 2 : nSRID = atoi(PQgetvalue(hResult, iRecord, 5));
1246 2 : ePostgisType = static_cast<PostgisType>(
1247 2 : atoi(PQgetvalue(hResult, iRecord, 6)));
1248 2 : bNullable = EQUAL(PQgetvalue(hResult, iRecord, 7), "f");
1249 2 : pszDescription = PQgetvalue(hResult, iRecord, 8);
1250 : }
1251 : else
1252 : {
1253 8289 : pszDescription = PQgetvalue(hResult, iRecord, 2);
1254 : }
1255 :
1256 8291 : if (EQUAL(pszTable, "spatial_ref_sys") ||
1257 8173 : EQUAL(pszTable, "geometry_columns") ||
1258 8057 : EQUAL(pszTable, "geography_columns"))
1259 350 : continue;
1260 :
1261 7941 : if (EQUAL(pszSchemaName, "information_schema") ||
1262 393 : EQUAL(pszSchemaName, "ogr_system_tables"))
1263 7657 : continue;
1264 :
1265 284 : int GeomTypeFlags = 0;
1266 284 : if (nGeomCoordDimension == 3)
1267 : {
1268 1 : if (bHasM)
1269 0 : GeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
1270 : else
1271 1 : GeomTypeFlags |= OGRGeometry::OGR_G_3D;
1272 : }
1273 283 : else if (nGeomCoordDimension == 4)
1274 : {
1275 0 : GeomTypeFlags |=
1276 : OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
1277 : }
1278 :
1279 568 : papsTables = static_cast<PGTableEntry **>(CPLRealloc(
1280 284 : papsTables, sizeof(PGTableEntry *) * (nTableCount + 1)));
1281 568 : papsTables[nTableCount] =
1282 284 : static_cast<PGTableEntry *>(CPLCalloc(1, sizeof(PGTableEntry)));
1283 284 : papsTables[nTableCount]->pszTableName = CPLStrdup(pszTable);
1284 284 : papsTables[nTableCount]->pszSchemaName = CPLStrdup(pszSchemaName);
1285 568 : papsTables[nTableCount]->pszDescription =
1286 284 : CPLStrdup(pszDescription ? pszDescription : "");
1287 284 : if (pszGeomColumnName)
1288 2 : OGRPGTableEntryAddGeomColumn(
1289 2 : papsTables[nTableCount], pszGeomColumnName, pszGeomType,
1290 : GeomTypeFlags, nSRID, ePostgisType, bNullable);
1291 284 : nTableCount++;
1292 :
1293 : PGTableEntry *psEntry =
1294 284 : OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName);
1295 284 : if (psEntry == nullptr)
1296 284 : psEntry = OGRPGAddTableEntry(hSetTables, pszTable,
1297 : pszSchemaName, pszDescription);
1298 284 : if (pszGeomColumnName)
1299 2 : OGRPGTableEntryAddGeomColumn(psEntry, pszGeomColumnName,
1300 : pszGeomType, GeomTypeFlags, nSRID,
1301 : ePostgisType, bNullable);
1302 : }
1303 :
1304 : /* --------------------------------------------------------------------
1305 : */
1306 : /* Cleanup */
1307 : /* --------------------------------------------------------------------
1308 : */
1309 119 : OGRPGClearResult(hResult);
1310 : }
1311 :
1312 : /* -------------------------------------------------------------------- */
1313 : /* Register the available tables. */
1314 : /* -------------------------------------------------------------------- */
1315 639 : for (int iRecord = 0; iRecord < nTableCount; iRecord++)
1316 : {
1317 : const PGTableEntry *psEntry = static_cast<PGTableEntry *>(
1318 366 : CPLHashSetLookup(hSetTables, papsTables[iRecord]));
1319 :
1320 : /* If SCHEMAS= is specified, only take into account tables inside */
1321 : /* one of the specified schemas */
1322 726 : if (papszSchemaList != nullptr &&
1323 360 : CSLFindString(papszSchemaList,
1324 360 : papsTables[iRecord]->pszSchemaName) == -1)
1325 : {
1326 278 : continue;
1327 : }
1328 :
1329 126 : CPLString osDefnName;
1330 :
1331 252 : if (papsTables[iRecord]->pszSchemaName &&
1332 126 : osCurrentSchema != papsTables[iRecord]->pszSchemaName)
1333 : {
1334 6 : osDefnName.Printf("%s.%s", papsTables[iRecord]->pszSchemaName,
1335 6 : papsTables[iRecord]->pszTableName);
1336 : }
1337 : else
1338 : {
1339 : // no prefix for current_schema in layer name, for backwards
1340 : // compatibility
1341 120 : osDefnName = papsTables[iRecord]->pszTableName;
1342 : }
1343 126 : if (osRegisteredLayers.find(osDefnName) != osRegisteredLayers.end())
1344 38 : continue;
1345 88 : osRegisteredLayers.insert(osDefnName);
1346 :
1347 176 : OGRPGTableLayer *poLayer = OpenTable(
1348 88 : osCurrentSchema, papsTables[iRecord]->pszTableName,
1349 88 : papsTables[iRecord]->pszSchemaName,
1350 88 : papsTables[iRecord]->pszDescription, nullptr, bDSUpdate, FALSE);
1351 88 : if (psEntry != nullptr)
1352 : {
1353 82 : if (psEntry->nGeomColumnCount > 0)
1354 : {
1355 44 : poLayer->SetGeometryInformation(psEntry->pasGeomColumns,
1356 44 : psEntry->nGeomColumnCount);
1357 : }
1358 : }
1359 : else
1360 : {
1361 6 : if (papsTables[iRecord]->nGeomColumnCount > 0)
1362 : {
1363 0 : poLayer->SetGeometryInformation(
1364 0 : papsTables[iRecord]->pasGeomColumns,
1365 0 : papsTables[iRecord]->nGeomColumnCount);
1366 : }
1367 : }
1368 : }
1369 :
1370 273 : end:
1371 273 : if (hSetTables)
1372 273 : CPLHashSetDestroy(hSetTables);
1373 :
1374 639 : for (int i = 0; i < nTableCount; i++)
1375 366 : OGRPGFreeTableEntry(papsTables[i]);
1376 273 : CPLFree(papsTables);
1377 : }
1378 :
1379 : /************************************************************************/
1380 : /* OpenTable() */
1381 : /************************************************************************/
1382 :
1383 498 : OGRPGTableLayer *OGRPGDataSource::OpenTable(CPLString &osCurrentSchemaIn,
1384 : const char *pszNewName,
1385 : const char *pszSchemaName,
1386 : const char *pszDescription,
1387 : const char *pszGeomColumnForced,
1388 : int bUpdate, int bTestOpen)
1389 :
1390 : {
1391 : /* -------------------------------------------------------------------- */
1392 : /* Create the layer object. */
1393 : /* -------------------------------------------------------------------- */
1394 : OGRPGTableLayer *poLayer =
1395 : new OGRPGTableLayer(this, osCurrentSchemaIn, pszNewName, pszSchemaName,
1396 498 : pszDescription, pszGeomColumnForced, bUpdate);
1397 498 : if (bTestOpen && !(poLayer->ReadTableDefinition()))
1398 : {
1399 227 : delete poLayer;
1400 227 : return nullptr;
1401 : }
1402 :
1403 : /* -------------------------------------------------------------------- */
1404 : /* Add layer to data source layer list. */
1405 : /* -------------------------------------------------------------------- */
1406 271 : papoLayers = static_cast<OGRPGTableLayer **>(
1407 271 : CPLRealloc(papoLayers, sizeof(OGRPGTableLayer *) * (nLayers + 1)));
1408 271 : papoLayers[nLayers++] = poLayer;
1409 :
1410 271 : return poLayer;
1411 : }
1412 :
1413 : /************************************************************************/
1414 : /* DeleteLayer() */
1415 : /************************************************************************/
1416 :
1417 24 : OGRErr OGRPGDataSource::DeleteLayer(int iLayer)
1418 :
1419 : {
1420 : /* Force loading of all registered tables */
1421 24 : GetLayerCount();
1422 24 : if (iLayer < 0 || iLayer >= nLayers)
1423 0 : return OGRERR_FAILURE;
1424 :
1425 24 : EndCopy();
1426 :
1427 : /* -------------------------------------------------------------------- */
1428 : /* Blow away our OGR structures related to the layer. This is */
1429 : /* pretty dangerous if anything has a reference to this layer! */
1430 : /* -------------------------------------------------------------------- */
1431 48 : CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName();
1432 48 : CPLString osTableName = papoLayers[iLayer]->GetTableName();
1433 48 : CPLString osSchemaName = papoLayers[iLayer]->GetSchemaName();
1434 :
1435 24 : CPLDebug("PG", "DeleteLayer(%s)", osLayerName.c_str());
1436 :
1437 24 : delete papoLayers[iLayer];
1438 24 : memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
1439 24 : sizeof(void *) * (nLayers - iLayer - 1));
1440 24 : nLayers--;
1441 :
1442 24 : if (osLayerName.empty())
1443 0 : return OGRERR_NONE;
1444 :
1445 : /* -------------------------------------------------------------------- */
1446 : /* Remove from the database. */
1447 : /* -------------------------------------------------------------------- */
1448 24 : CPLString osCommand;
1449 :
1450 24 : SoftStartTransaction();
1451 :
1452 : osCommand.Printf("DROP TABLE %s.%s CASCADE",
1453 48 : OGRPGEscapeColumnName(osSchemaName).c_str(),
1454 72 : OGRPGEscapeColumnName(osTableName).c_str());
1455 24 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1456 24 : OGRPGClearResult(hResult);
1457 :
1458 24 : SoftCommitTransaction();
1459 :
1460 24 : return OGRERR_NONE;
1461 : }
1462 :
1463 : /************************************************************************/
1464 : /* ICreateLayer() */
1465 : /************************************************************************/
1466 :
1467 226 : OGRLayer *OGRPGDataSource::ICreateLayer(const char *pszLayerName,
1468 : const OGRGeomFieldDefn *poGeomFieldDefn,
1469 : CSLConstList papszOptions)
1470 :
1471 : {
1472 226 : const char *pszGeomType = nullptr;
1473 226 : char *pszTableName = nullptr;
1474 226 : char *pszSchemaName = nullptr;
1475 226 : int GeometryTypeFlags = 0;
1476 :
1477 226 : auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
1478 : const auto poSRS =
1479 226 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
1480 :
1481 226 : if (pszLayerName == nullptr)
1482 0 : return nullptr;
1483 :
1484 226 : EndCopy();
1485 :
1486 : const bool bUTF8ToASCII =
1487 226 : CPLFetchBool(papszOptions, "LAUNDER_ASCII", false);
1488 : const bool bLaunder =
1489 226 : bUTF8ToASCII || CPLFetchBool(papszOptions, "LAUNDER", true);
1490 :
1491 226 : const char *pszFIDColumnNameIn = CSLFetchNameValue(papszOptions, "FID");
1492 452 : CPLString osFIDColumnName;
1493 226 : if (pszFIDColumnNameIn == nullptr)
1494 224 : osFIDColumnName = "ogc_fid";
1495 : else
1496 : {
1497 2 : if (bLaunder)
1498 : {
1499 : char *pszLaunderedFid =
1500 2 : OGRPGCommonLaunderName(pszFIDColumnNameIn, "PG", bUTF8ToASCII);
1501 2 : osFIDColumnName += pszLaunderedFid;
1502 2 : CPLFree(pszLaunderedFid);
1503 : }
1504 : else
1505 0 : osFIDColumnName += pszFIDColumnNameIn;
1506 : }
1507 452 : CPLString osFIDColumnNameEscaped = OGRPGEscapeColumnName(osFIDColumnName);
1508 :
1509 226 : if (STARTS_WITH(pszLayerName, "pg"))
1510 : {
1511 0 : CPLError(CE_Warning, CPLE_AppDefined,
1512 : "The layer name should not begin by 'pg' as it is a reserved "
1513 : "prefix");
1514 : }
1515 :
1516 226 : if (OGR_GT_HasZ(eType))
1517 5 : GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
1518 226 : if (OGR_GT_HasM(eType))
1519 2 : GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
1520 :
1521 226 : int ForcedGeometryTypeFlags = -1;
1522 226 : const char *pszDim = CSLFetchNameValue(papszOptions, "DIM");
1523 226 : if (pszDim != nullptr)
1524 : {
1525 75 : if (EQUAL(pszDim, "XY") || EQUAL(pszDim, "2"))
1526 : {
1527 0 : GeometryTypeFlags = 0;
1528 0 : ForcedGeometryTypeFlags = GeometryTypeFlags;
1529 : }
1530 75 : else if (EQUAL(pszDim, "XYZ") || EQUAL(pszDim, "3"))
1531 : {
1532 69 : GeometryTypeFlags = OGRGeometry::OGR_G_3D;
1533 69 : ForcedGeometryTypeFlags = GeometryTypeFlags;
1534 : }
1535 6 : else if (EQUAL(pszDim, "XYM"))
1536 : {
1537 3 : GeometryTypeFlags = OGRGeometry::OGR_G_MEASURED;
1538 3 : ForcedGeometryTypeFlags = GeometryTypeFlags;
1539 : }
1540 3 : else if (EQUAL(pszDim, "XYZM") || EQUAL(pszDim, "4"))
1541 : {
1542 3 : GeometryTypeFlags =
1543 : OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
1544 3 : ForcedGeometryTypeFlags = GeometryTypeFlags;
1545 : }
1546 : else
1547 : {
1548 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for DIM");
1549 : }
1550 : }
1551 :
1552 : /* Should we turn layers with None geometry type as Unknown/GEOMETRY */
1553 : /* so they are still recorded in geometry_columns table ? (#4012) */
1554 226 : int bNoneAsUnknown = CPLTestBool(
1555 226 : CSLFetchNameValueDef(papszOptions, "NONE_AS_UNKNOWN", "NO"));
1556 226 : if (bNoneAsUnknown && eType == wkbNone)
1557 0 : eType = wkbUnknown;
1558 :
1559 226 : int bExtractSchemaFromLayerName = CPLTestBool(CSLFetchNameValueDef(
1560 226 : papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
1561 :
1562 : /* Postgres Schema handling:
1563 : Extract schema name from input layer name or passed with -lco SCHEMA.
1564 : Set layer name to "schema.table" or to "table" if schema ==
1565 : current_schema() Usage without schema name is backwards compatible
1566 : */
1567 226 : const char *pszDotPos = strstr(pszLayerName, ".");
1568 226 : if (pszDotPos != nullptr && bExtractSchemaFromLayerName)
1569 : {
1570 19 : int length = static_cast<int>(pszDotPos - pszLayerName);
1571 19 : pszSchemaName = static_cast<char *>(CPLMalloc(length + 1));
1572 19 : strncpy(pszSchemaName, pszLayerName, length);
1573 19 : pszSchemaName[length] = '\0';
1574 :
1575 19 : if (bLaunder)
1576 17 : pszTableName = OGRPGCommonLaunderName(pszDotPos + 1, "PG",
1577 : bUTF8ToASCII); // skip "."
1578 : else
1579 2 : pszTableName = CPLStrdup(
1580 4 : OGRPGCommonGenerateShortEnoughIdentifier(pszDotPos + 1)
1581 19 : .c_str()); // skip "."
1582 : }
1583 : else
1584 : {
1585 207 : pszSchemaName = nullptr;
1586 207 : if (bLaunder)
1587 207 : pszTableName = OGRPGCommonLaunderName(pszLayerName, "PG",
1588 : bUTF8ToASCII); // skip "."
1589 : else
1590 : pszTableName =
1591 0 : CPLStrdup(OGRPGCommonGenerateShortEnoughIdentifier(pszLayerName)
1592 : .c_str()); // skip "."
1593 : }
1594 :
1595 : /* -------------------------------------------------------------------- */
1596 : /* Set the default schema for the layers. */
1597 : /* -------------------------------------------------------------------- */
1598 226 : if (CSLFetchNameValue(papszOptions, "SCHEMA") != nullptr)
1599 : {
1600 3 : CPLFree(pszSchemaName);
1601 3 : pszSchemaName = CPLStrdup(CSLFetchNameValue(papszOptions, "SCHEMA"));
1602 : }
1603 :
1604 226 : const bool bTemporary = CPLFetchBool(papszOptions, "TEMPORARY", false);
1605 226 : if (bTemporary)
1606 : {
1607 1 : CPLFree(pszSchemaName);
1608 1 : pszSchemaName = CPLStrdup("pg_temp");
1609 : }
1610 226 : if (pszSchemaName == nullptr)
1611 : {
1612 205 : pszSchemaName = CPLStrdup(osCurrentSchema);
1613 : }
1614 :
1615 : // Check that the schema exist. If there is a single match in a case
1616 : // insensitive way, use it. Otherwise error out if the match is not exact.
1617 : {
1618 226 : const auto osNewSchemaName = FindSchema(pszSchemaName);
1619 226 : if (!osNewSchemaName.has_value())
1620 : {
1621 4 : CPLFree(pszTableName);
1622 4 : CPLFree(pszSchemaName);
1623 4 : return nullptr;
1624 : }
1625 222 : CPLFree(pszSchemaName);
1626 222 : pszSchemaName = CPLStrdup(osNewSchemaName->c_str());
1627 : }
1628 :
1629 : /* -------------------------------------------------------------------- */
1630 : /* Do we already have this layer? If so, should we blow it */
1631 : /* away? */
1632 : /* -------------------------------------------------------------------- */
1633 444 : CPLString osSQLLayerName;
1634 444 : if (pszSchemaName == nullptr ||
1635 222 : (!osCurrentSchema.empty() &&
1636 222 : EQUAL(pszSchemaName, osCurrentSchema.c_str())))
1637 205 : osSQLLayerName = pszTableName;
1638 : else
1639 : {
1640 17 : osSQLLayerName = pszSchemaName;
1641 17 : osSQLLayerName += ".";
1642 17 : osSQLLayerName += pszTableName;
1643 : }
1644 :
1645 : /* GetLayerByName() can instantiate layers that would have been */
1646 : /* 'hidden' otherwise, for example, non-spatial tables in a */
1647 : /* PostGIS-enabled database, so this apparently useless command is */
1648 : /* not useless. (#4012) */
1649 222 : CPLPushErrorHandler(CPLQuietErrorHandler);
1650 222 : GetLayerByName(osSQLLayerName);
1651 222 : CPLPopErrorHandler();
1652 222 : CPLErrorReset();
1653 :
1654 : /* Force loading of all registered tables */
1655 222 : GetLayerCount();
1656 :
1657 308 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
1658 : {
1659 88 : if (EQUAL(osSQLLayerName.c_str(), papoLayers[iLayer]->GetName()))
1660 : {
1661 30 : if (CSLFetchNameValue(papszOptions, "OVERWRITE") != nullptr &&
1662 14 : !EQUAL(CSLFetchNameValue(papszOptions, "OVERWRITE"), "NO"))
1663 : {
1664 14 : DeleteLayer(iLayer);
1665 : }
1666 : else
1667 : {
1668 2 : CPLError(CE_Failure, CPLE_AppDefined,
1669 : "Layer %s already exists, CreateLayer failed.\n"
1670 : "Use the layer creation option OVERWRITE=YES to "
1671 : "replace it.",
1672 : osSQLLayerName.c_str());
1673 2 : CPLFree(pszTableName);
1674 2 : CPLFree(pszSchemaName);
1675 2 : return nullptr;
1676 : }
1677 : }
1678 : }
1679 :
1680 : /* -------------------------------------------------------------------- */
1681 : /* Handle the GEOM_TYPE option. */
1682 : /* -------------------------------------------------------------------- */
1683 220 : pszGeomType = CSLFetchNameValue(papszOptions, "GEOM_TYPE");
1684 220 : if (pszGeomType == nullptr)
1685 : {
1686 214 : if (bHavePostGIS)
1687 127 : pszGeomType = "geometry";
1688 : else
1689 87 : pszGeomType = "bytea";
1690 : }
1691 :
1692 220 : const char *pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
1693 220 : if (eType != wkbNone && EQUAL(pszGeomType, "geography"))
1694 : {
1695 5 : if (!bHaveGeography)
1696 : {
1697 0 : CPLError(
1698 : CE_Failure, CPLE_AppDefined,
1699 : "GEOM_TYPE=geography is only supported in PostGIS >= 1.5.\n"
1700 : "Creation of layer %s has failed.",
1701 : pszLayerName);
1702 0 : CPLFree(pszTableName);
1703 0 : CPLFree(pszSchemaName);
1704 0 : return nullptr;
1705 : }
1706 :
1707 5 : if (pszGFldName == nullptr)
1708 2 : pszGFldName = "the_geog";
1709 : }
1710 215 : else if (eType != wkbNone && bHavePostGIS &&
1711 107 : !EQUAL(pszGeomType, "geography"))
1712 : {
1713 107 : if (pszGFldName == nullptr)
1714 105 : pszGFldName = "wkb_geometry";
1715 : }
1716 :
1717 220 : if (eType != wkbNone && bHavePostGIS && !EQUAL(pszGeomType, "geometry") &&
1718 5 : !EQUAL(pszGeomType, "geography"))
1719 : {
1720 0 : if (bHaveGeography)
1721 0 : CPLError(CE_Failure, CPLE_AppDefined,
1722 : "GEOM_TYPE in PostGIS enabled databases must be "
1723 : "'geometry' or 'geography'.\n"
1724 : "Creation of layer %s with GEOM_TYPE %s has failed.",
1725 : pszLayerName, pszGeomType);
1726 : else
1727 0 : CPLError(
1728 : CE_Failure, CPLE_AppDefined,
1729 : "GEOM_TYPE in PostGIS enabled databases must be 'geometry'.\n"
1730 : "Creation of layer %s with GEOM_TYPE %s has failed.",
1731 : pszLayerName, pszGeomType);
1732 :
1733 0 : CPLFree(pszTableName);
1734 0 : CPLFree(pszSchemaName);
1735 0 : return nullptr;
1736 : }
1737 :
1738 : /* -------------------------------------------------------------------- */
1739 : /* Try to get the SRS Id of this spatial reference system, */
1740 : /* adding tot the srs table if needed. */
1741 : /* -------------------------------------------------------------------- */
1742 220 : int nSRSId = nUndefinedSRID;
1743 :
1744 220 : if (poSRS != nullptr)
1745 12 : nSRSId = FetchSRSId(poSRS);
1746 :
1747 220 : if (eType != wkbNone && EQUAL(pszGeomType, "geography"))
1748 : {
1749 5 : if (poSRS != nullptr && !poSRS->IsGeographic())
1750 : {
1751 1 : CPLError(CE_Failure, CPLE_NotSupported,
1752 : "geography type only supports geographic SRS");
1753 :
1754 1 : CPLFree(pszTableName);
1755 1 : CPLFree(pszSchemaName);
1756 1 : return nullptr;
1757 : }
1758 :
1759 4 : if (!(sPostGISVersion.nMajor >= 3 ||
1760 0 : (sPostGISVersion.nMajor == 2 && sPostGISVersion.nMinor >= 2)) &&
1761 0 : nSRSId != nUndefinedSRID && nSRSId != 4326)
1762 : {
1763 0 : CPLError(CE_Failure, CPLE_NotSupported,
1764 : "geography type in PostGIS < 2.2 only supports SRS = "
1765 : "EPSG:4326");
1766 :
1767 0 : CPLFree(pszTableName);
1768 0 : CPLFree(pszSchemaName);
1769 0 : return nullptr;
1770 : }
1771 :
1772 4 : if (nSRSId == nUndefinedSRID)
1773 : {
1774 3 : CPLError(CE_Warning, CPLE_AppDefined,
1775 : "Assuming EPSG:4326 for geographic type (but no implicit "
1776 : "reprojection to it will be done)");
1777 3 : nSRSId = 4326;
1778 : }
1779 : }
1780 :
1781 219 : const char *pszGeometryType = OGRToOGCGeomType(eType);
1782 :
1783 : int bDeferredCreation =
1784 219 : CPLTestBool(CPLGetConfigOption("OGR_PG_DEFERRED_CREATION", "YES"));
1785 219 : if (!bHavePostGIS)
1786 87 : bDeferredCreation =
1787 : FALSE; /* to avoid unnecessary implementation and testing burden */
1788 :
1789 : /* -------------------------------------------------------------------- */
1790 : /* Create a basic table with the FID. Also include the */
1791 : /* geometry if this is not a PostGIS enabled table. */
1792 : /* -------------------------------------------------------------------- */
1793 219 : const bool bFID64 = CPLFetchBool(papszOptions, "FID64", false);
1794 219 : const char *pszSerialType = bFID64 ? "BIGSERIAL" : "SERIAL";
1795 :
1796 438 : CPLString osCreateTable;
1797 219 : if (bTemporary)
1798 : {
1799 : osCreateTable.Printf("CREATE TEMPORARY TABLE %s",
1800 1 : OGRPGEscapeColumnName(pszTableName).c_str());
1801 : }
1802 : else
1803 : {
1804 : osCreateTable.Printf(
1805 : "CREATE%s TABLE %s.%s",
1806 218 : CPLFetchBool(papszOptions, "UNLOGGED", false) ? " UNLOGGED" : "",
1807 436 : OGRPGEscapeColumnName(pszSchemaName).c_str(),
1808 872 : OGRPGEscapeColumnName(pszTableName).c_str());
1809 : }
1810 :
1811 219 : const char *suffix = nullptr;
1812 219 : if ((GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
1813 77 : (GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
1814 4 : suffix = "ZM";
1815 219 : else if ((GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) &&
1816 4 : (EQUAL(pszGeomType, "geography") ||
1817 3 : wkbFlatten(eType) != wkbUnknown))
1818 2 : suffix = "M";
1819 213 : else if (GeometryTypeFlags & OGRGeometry::OGR_G_3D)
1820 73 : suffix = "Z";
1821 : else
1822 140 : suffix = "";
1823 :
1824 : {
1825 438 : CPLString osCommand;
1826 219 : if (eType != wkbNone && !bHavePostGIS)
1827 : {
1828 79 : if (pszGFldName && !EQUAL(pszGFldName, "wkb_geometry"))
1829 : {
1830 3 : CPLError(CE_Warning, CPLE_AppDefined,
1831 : "GEOMETRY_NAME=%s ignored, and set instead to "
1832 : "'wkb_geometry' as it is the only geometry column "
1833 : "name recognized for non-PostGIS enabled databases.",
1834 : pszGFldName);
1835 : }
1836 79 : pszGFldName = "wkb_geometry";
1837 79 : osCommand.Printf("%s ( "
1838 : " %s %s, "
1839 : " %s %s, "
1840 : " PRIMARY KEY (%s)",
1841 : osCreateTable.c_str(),
1842 : osFIDColumnNameEscaped.c_str(), pszSerialType,
1843 : pszGFldName, pszGeomType,
1844 79 : osFIDColumnNameEscaped.c_str());
1845 : }
1846 140 : else if (!bDeferredCreation && eType != wkbNone &&
1847 2 : EQUAL(pszGeomType, "geography"))
1848 : {
1849 1 : osCommand.Printf(
1850 : "%s ( %s %s, %s geography(%s%s%s), PRIMARY KEY (%s)",
1851 : osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
1852 2 : pszSerialType, OGRPGEscapeColumnName(pszGFldName).c_str(),
1853 : pszGeometryType, suffix,
1854 1 : nSRSId ? CPLSPrintf(",%d", nSRSId) : "",
1855 2 : osFIDColumnNameEscaped.c_str());
1856 : }
1857 139 : else if (!bDeferredCreation && eType != wkbNone &&
1858 1 : !EQUAL(pszGeomType, "geography") &&
1859 1 : sPostGISVersion.nMajor >= 2)
1860 : {
1861 1 : osCommand.Printf(
1862 : "%s ( %s %s, %s geometry(%s%s%s), PRIMARY KEY (%s)",
1863 : osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(),
1864 2 : pszSerialType, OGRPGEscapeColumnName(pszGFldName).c_str(),
1865 : pszGeometryType, suffix,
1866 0 : nSRSId ? CPLSPrintf(",%d", nSRSId) : "",
1867 2 : osFIDColumnNameEscaped.c_str());
1868 : }
1869 : else
1870 : {
1871 : osCommand.Printf("%s ( %s %s, PRIMARY KEY (%s)",
1872 : osCreateTable.c_str(),
1873 : osFIDColumnNameEscaped.c_str(), pszSerialType,
1874 138 : osFIDColumnNameEscaped.c_str());
1875 : }
1876 219 : osCreateTable = std::move(osCommand);
1877 : }
1878 :
1879 : const char *pszSI =
1880 219 : CSLFetchNameValueDef(papszOptions, "SPATIAL_INDEX", "GIST");
1881 219 : bool bCreateSpatialIndex =
1882 0 : (EQUAL(pszSI, "GIST") || EQUAL(pszSI, "SPGIST") ||
1883 219 : EQUAL(pszSI, "BRIN") || EQUAL(pszSI, "YES") || EQUAL(pszSI, "ON") ||
1884 0 : EQUAL(pszSI, "TRUE"));
1885 219 : if (!bCreateSpatialIndex && !EQUAL(pszSI, "NO") && !EQUAL(pszSI, "OFF") &&
1886 0 : !EQUAL(pszSI, "FALSE") && !EQUAL(pszSI, "NONE"))
1887 : {
1888 0 : CPLError(CE_Warning, CPLE_NotSupported,
1889 : "SPATIAL_INDEX=%s not supported", pszSI);
1890 : }
1891 438 : const char *pszSpatialIndexType = EQUAL(pszSI, "SPGIST") ? "SPGIST"
1892 219 : : EQUAL(pszSI, "BRIN") ? "BRIN"
1893 : : "GIST";
1894 190 : if (eType != wkbNone && bCreateSpatialIndex &&
1895 409 : CPLFetchBool(papszOptions, "UNLOGGED", false) &&
1896 0 : !(sPostgreSQLVersion.nMajor > 9 ||
1897 0 : (sPostgreSQLVersion.nMajor == 9 && sPostgreSQLVersion.nMinor >= 3)))
1898 : {
1899 0 : CPLError(
1900 : CE_Warning, CPLE_NotSupported,
1901 : "GiST index only supported since Postgres 9.3 on unlogged table");
1902 0 : bCreateSpatialIndex = false;
1903 : }
1904 :
1905 219 : if (!bDeferredCreation)
1906 : {
1907 89 : SoftStartTransaction();
1908 :
1909 89 : CPLString osCommand = osCreateTable;
1910 89 : osCommand += " )";
1911 :
1912 89 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1913 89 : if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
1914 : {
1915 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
1916 0 : PQerrorMessage(hPGConn));
1917 0 : CPLFree(pszTableName);
1918 0 : CPLFree(pszSchemaName);
1919 :
1920 0 : OGRPGClearResult(hResult);
1921 :
1922 0 : SoftRollbackTransaction();
1923 0 : return nullptr;
1924 : }
1925 :
1926 89 : OGRPGClearResult(hResult);
1927 :
1928 89 : if (eType != wkbNone && bHavePostGIS && bCreateSpatialIndex)
1929 : {
1930 : // Create the spatial index.
1931 : // We're doing this before we add geometry and record to the
1932 : // table, so this may not be exactly the best way to do it.
1933 : const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
1934 2 : pszTableName, pszGFldName, 0));
1935 :
1936 : osCommand.Printf("CREATE INDEX %s ON %s.%s USING %s (%s)",
1937 4 : OGRPGEscapeColumnName(osIndexName.c_str()).c_str(),
1938 4 : OGRPGEscapeColumnName(pszSchemaName).c_str(),
1939 4 : OGRPGEscapeColumnName(pszTableName).c_str(),
1940 : pszSpatialIndexType,
1941 10 : OGRPGEscapeColumnName(pszGFldName).c_str());
1942 :
1943 2 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
1944 :
1945 2 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
1946 : {
1947 0 : CPLError(CE_Failure, CPLE_AppDefined,
1948 : "'%s' failed for layer %s, index creation has failed.",
1949 : osCommand.c_str(), pszLayerName);
1950 :
1951 0 : CPLFree(pszTableName);
1952 0 : CPLFree(pszSchemaName);
1953 :
1954 0 : OGRPGClearResult(hResult);
1955 :
1956 0 : SoftRollbackTransaction();
1957 :
1958 0 : return nullptr;
1959 : }
1960 2 : OGRPGClearResult(hResult);
1961 : }
1962 :
1963 : /* --------------------------------------------------------------------
1964 : */
1965 : /* Complete, and commit the transaction. */
1966 : /* --------------------------------------------------------------------
1967 : */
1968 89 : SoftCommitTransaction();
1969 : }
1970 :
1971 : /* -------------------------------------------------------------------- */
1972 : /* Create the layer object. */
1973 : /* -------------------------------------------------------------------- */
1974 : OGRPGTableLayer *poLayer = new OGRPGTableLayer(
1975 219 : this, osCurrentSchema, pszTableName, pszSchemaName, "", nullptr, TRUE);
1976 219 : poLayer->SetTableDefinition(osFIDColumnName, pszGFldName, eType,
1977 : pszGeomType, nSRSId, GeometryTypeFlags);
1978 219 : poLayer->SetLaunderFlag(bLaunder);
1979 219 : poLayer->SetUTF8ToASCIIFlag(bUTF8ToASCII);
1980 219 : poLayer->SetPrecisionFlag(CPLFetchBool(papszOptions, "PRECISION", true));
1981 : // poLayer->SetForcedSRSId(nForcedSRSId);
1982 219 : poLayer->SetForcedGeometryTypeFlags(ForcedGeometryTypeFlags);
1983 219 : poLayer->SetCreateSpatialIndex(bCreateSpatialIndex, pszSpatialIndexType);
1984 219 : poLayer->SetDeferredCreation(bDeferredCreation, osCreateTable);
1985 :
1986 219 : const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
1987 219 : if (pszDescription != nullptr)
1988 2 : poLayer->SetForcedDescription(pszDescription);
1989 :
1990 : /* HSTORE_COLUMNS existed at a time during GDAL 1.10dev */
1991 : const char *pszHSTOREColumns =
1992 219 : CSLFetchNameValue(papszOptions, "HSTORE_COLUMNS");
1993 219 : if (pszHSTOREColumns != nullptr)
1994 0 : CPLError(CE_Warning, CPLE_AppDefined,
1995 : "HSTORE_COLUMNS not recognized. Use COLUMN_TYPES instead.");
1996 :
1997 : const char *pszOverrideColumnTypes =
1998 219 : CSLFetchNameValue(papszOptions, "COLUMN_TYPES");
1999 219 : poLayer->SetOverrideColumnTypes(pszOverrideColumnTypes);
2000 :
2001 219 : poLayer->AllowAutoFIDOnCreateViaCopy();
2002 219 : if (CPLTestBool(CPLGetConfigOption("PG_USE_COPY", "YES")))
2003 213 : poLayer->SetUseCopy();
2004 :
2005 219 : if (bFID64)
2006 2 : poLayer->SetMetadataItem(OLMD_FID64, "YES");
2007 :
2008 : /* -------------------------------------------------------------------- */
2009 : /* Add layer to data source layer list. */
2010 : /* -------------------------------------------------------------------- */
2011 219 : papoLayers = static_cast<OGRPGTableLayer **>(
2012 219 : CPLRealloc(papoLayers, sizeof(OGRPGTableLayer *) * (nLayers + 1)));
2013 :
2014 219 : papoLayers[nLayers++] = poLayer;
2015 :
2016 219 : CPLFree(pszTableName);
2017 219 : CPLFree(pszSchemaName);
2018 :
2019 219 : return poLayer;
2020 : }
2021 :
2022 : /************************************************************************/
2023 : /* TestCapability() */
2024 : /************************************************************************/
2025 :
2026 115 : int OGRPGDataSource::TestCapability(const char *pszCap)
2027 :
2028 : {
2029 115 : if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer) ||
2030 84 : EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
2031 51 : return TRUE;
2032 64 : else if (EQUAL(pszCap, ODsCCurveGeometries))
2033 12 : return TRUE;
2034 52 : else if (EQUAL(pszCap, ODsCTransactions))
2035 20 : return TRUE;
2036 32 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
2037 12 : return TRUE;
2038 20 : else if (EQUAL(pszCap, ODsCZGeometries))
2039 14 : return TRUE;
2040 6 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
2041 0 : return TRUE;
2042 : else
2043 6 : return FALSE;
2044 : }
2045 :
2046 : /************************************************************************/
2047 : /* GetLayerCount() */
2048 : /************************************************************************/
2049 :
2050 1243 : int OGRPGDataSource::GetLayerCount()
2051 : {
2052 1243 : LoadTables();
2053 1243 : return nLayers;
2054 : }
2055 :
2056 : /************************************************************************/
2057 : /* GetLayer() */
2058 : /************************************************************************/
2059 :
2060 311 : OGRLayer *OGRPGDataSource::GetLayer(int iLayer)
2061 :
2062 : {
2063 : /* Force loading of all registered tables */
2064 311 : if (iLayer < 0 || iLayer >= GetLayerCount())
2065 10 : return nullptr;
2066 : else
2067 301 : return papoLayers[iLayer];
2068 : }
2069 :
2070 : /************************************************************************/
2071 : /* FindSchema() */
2072 : /************************************************************************/
2073 :
2074 : // Check that the schema exists. If there is a single match in a case
2075 : // insensitive way, use it. Otherwise error out if the match is not exact.
2076 : // Return the schema name with its exact case from pg_catalog, or an empty
2077 : // string if an error occurs.
2078 : std::optional<std::string>
2079 282 : OGRPGDataSource::FindSchema(const char *pszSchemaNameIn)
2080 : {
2081 282 : if (strcmp(pszSchemaNameIn, "public") == 0 ||
2082 279 : strcmp(pszSchemaNameIn, "pg_temp") == 0)
2083 : {
2084 5 : return pszSchemaNameIn;
2085 : }
2086 :
2087 277 : EndCopy();
2088 :
2089 554 : std::string osSchemaName;
2090 : std::string osCommand(
2091 554 : "SELECT nspname FROM pg_catalog.pg_namespace WHERE nspname ILIKE ");
2092 277 : osCommand += OGRPGEscapeString(hPGConn, pszSchemaNameIn);
2093 277 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2094 277 : if (hResult && PQntuples(hResult) == 1)
2095 : {
2096 269 : osSchemaName = PQgetvalue(hResult, 0, 0);
2097 : }
2098 8 : else if (hResult)
2099 : {
2100 8 : const int nTuples = PQntuples(hResult);
2101 8 : if (nTuples == 0)
2102 : {
2103 2 : CPLError(CE_Failure, CPLE_AppDefined,
2104 : "Schema \"%s\" does not exist.", pszSchemaNameIn);
2105 2 : return {};
2106 : }
2107 12 : for (int i = 0; i < nTuples; ++i)
2108 : {
2109 10 : if (strcmp(PQgetvalue(hResult, i, 0), pszSchemaNameIn) == 0)
2110 : {
2111 4 : osSchemaName = pszSchemaNameIn;
2112 4 : break;
2113 : }
2114 : }
2115 6 : if (osSchemaName.empty())
2116 : {
2117 2 : CPLError(CE_Failure, CPLE_AppDefined,
2118 : "Several schemas exist whose name matches \"%s\", but "
2119 : "not with that case. "
2120 : "Please specify the schema name with the exact case.",
2121 : pszSchemaNameIn);
2122 2 : return {};
2123 : }
2124 : }
2125 273 : OGRPGClearResult(hResult);
2126 :
2127 273 : return osSchemaName;
2128 : }
2129 :
2130 : /************************************************************************/
2131 : /* GetLayerByName() */
2132 : /************************************************************************/
2133 :
2134 528 : OGRLayer *OGRPGDataSource::GetLayerByName(const char *pszNameIn)
2135 :
2136 : {
2137 528 : char *pszTableName = nullptr;
2138 528 : char *pszGeomColumnName = nullptr;
2139 528 : char *pszSchemaName = nullptr;
2140 :
2141 528 : if (!pszNameIn)
2142 0 : return nullptr;
2143 :
2144 : /* first a case sensitive check */
2145 : /* do NOT force loading of all registered tables */
2146 744 : for (int i = 0; i < nLayers; i++)
2147 : {
2148 312 : OGRPGTableLayer *poLayer = papoLayers[i];
2149 :
2150 312 : if (strcmp(pszNameIn, poLayer->GetName()) == 0)
2151 : {
2152 96 : return poLayer;
2153 : }
2154 : }
2155 :
2156 : /* then case insensitive */
2157 600 : for (int i = 0; i < nLayers; i++)
2158 : {
2159 168 : OGRPGTableLayer *poLayer = papoLayers[i];
2160 :
2161 168 : if (EQUAL(pszNameIn, poLayer->GetName()))
2162 : {
2163 0 : return poLayer;
2164 : }
2165 : }
2166 :
2167 432 : char *pszNameWithoutBracket = CPLStrdup(pszNameIn);
2168 432 : char *pos = strchr(pszNameWithoutBracket, '(');
2169 432 : if (pos != nullptr)
2170 : {
2171 5 : *pos = '\0';
2172 5 : pszGeomColumnName = CPLStrdup(pos + 1);
2173 5 : int len = static_cast<int>(strlen(pszGeomColumnName));
2174 5 : if (len > 0)
2175 5 : pszGeomColumnName[len - 1] = '\0';
2176 : }
2177 :
2178 432 : pos = strchr(pszNameWithoutBracket, '.');
2179 432 : if (pos != nullptr)
2180 : {
2181 56 : *pos = '\0';
2182 56 : const auto osSchemaName = FindSchema(pszNameWithoutBracket);
2183 56 : if (!osSchemaName.has_value())
2184 : {
2185 0 : CPLFree(pszNameWithoutBracket);
2186 0 : CPLFree(pszGeomColumnName);
2187 0 : return nullptr;
2188 : }
2189 56 : pszSchemaName = CPLStrdup(osSchemaName->c_str());
2190 56 : pszTableName = CPLStrdup(pos + 1);
2191 : }
2192 : else
2193 : {
2194 376 : pszTableName = CPLStrdup(pszNameWithoutBracket);
2195 : }
2196 :
2197 432 : if (strlen(pszTableName) > OGR_PG_NAMEDATALEN - 1)
2198 0 : pszTableName[OGR_PG_NAMEDATALEN - 1] = 0;
2199 :
2200 432 : CPLFree(pszNameWithoutBracket);
2201 432 : pszNameWithoutBracket = nullptr;
2202 :
2203 432 : OGRPGTableLayer *poLayer = nullptr;
2204 :
2205 432 : if (pszSchemaName != nullptr && osCurrentSchema == pszSchemaName &&
2206 : pszGeomColumnName == nullptr)
2207 : {
2208 : poLayer =
2209 26 : cpl::down_cast<OGRPGTableLayer *>(GetLayerByName(pszTableName));
2210 : }
2211 : else
2212 : {
2213 406 : EndCopy();
2214 :
2215 812 : const CPLString osTableName(pszTableName);
2216 812 : const CPLString osTableNameLower = CPLString(pszTableName).tolower();
2217 406 : if (osTableName != osTableNameLower)
2218 4 : CPLPushErrorHandler(CPLQuietErrorHandler);
2219 406 : poLayer = OpenTable(osCurrentSchema, pszTableName, pszSchemaName,
2220 : nullptr, pszGeomColumnName, bDSUpdate, TRUE);
2221 406 : if (osTableName != osTableNameLower)
2222 4 : CPLPopErrorHandler();
2223 406 : if (poLayer == nullptr && osTableName != osTableNameLower)
2224 : {
2225 : poLayer =
2226 4 : OpenTable(osCurrentSchema, osTableNameLower, pszSchemaName,
2227 : nullptr, pszGeomColumnName, bDSUpdate, TRUE);
2228 : }
2229 : }
2230 :
2231 432 : CPLFree(pszTableName);
2232 432 : CPLFree(pszSchemaName);
2233 432 : CPLFree(pszGeomColumnName);
2234 :
2235 432 : return poLayer;
2236 : }
2237 :
2238 : /************************************************************************/
2239 : /* OGRPGNoticeProcessor() */
2240 : /************************************************************************/
2241 :
2242 421 : static void OGRPGNoticeProcessor(CPL_UNUSED void *arg, const char *pszMessage)
2243 : {
2244 421 : CPLDebug("OGR_PG_NOTICE", "%s", pszMessage);
2245 421 : }
2246 :
2247 : /************************************************************************/
2248 : /* InitializeMetadataTables() */
2249 : /* */
2250 : /* Create the metadata tables (SPATIAL_REF_SYS and */
2251 : /* GEOMETRY_COLUMNS). */
2252 : /************************************************************************/
2253 :
2254 0 : OGRErr OGRPGDataSource::InitializeMetadataTables()
2255 :
2256 : {
2257 : // implement later.
2258 0 : return OGRERR_FAILURE;
2259 : }
2260 :
2261 : /************************************************************************/
2262 : /* FetchSRS() */
2263 : /* */
2264 : /* Return a SRS corresponding to a particular id. Note that */
2265 : /* reference counting should be honoured on the returned */
2266 : /* OGRSpatialReference, as handles may be cached. */
2267 : /************************************************************************/
2268 :
2269 33 : const OGRSpatialReference *OGRPGDataSource::FetchSRS(int nId)
2270 :
2271 : {
2272 33 : if (nId < 0 || !m_bHasSpatialRefSys)
2273 0 : return nullptr;
2274 :
2275 : /* -------------------------------------------------------------------- */
2276 : /* First, we look through our SRID cache, is it there? */
2277 : /* -------------------------------------------------------------------- */
2278 33 : auto oIter = m_oSRSCache.find(nId);
2279 33 : if (oIter != m_oSRSCache.end())
2280 : {
2281 6 : return oIter->second.get();
2282 : }
2283 :
2284 27 : EndCopy();
2285 :
2286 : /* -------------------------------------------------------------------- */
2287 : /* Try looking up in spatial_ref_sys table. */
2288 : /* -------------------------------------------------------------------- */
2289 54 : CPLString osCommand;
2290 27 : std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSRS;
2291 :
2292 27 : osCommand.Printf("SELECT srtext, auth_name, auth_srid FROM spatial_ref_sys "
2293 : "WHERE srid = %d",
2294 27 : nId);
2295 27 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2296 :
2297 54 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
2298 27 : PQntuples(hResult) == 1)
2299 : {
2300 27 : const char *pszWKT = PQgetvalue(hResult, 0, 0);
2301 27 : const char *pszAuthName = PQgetvalue(hResult, 0, 1);
2302 27 : const char *pszAuthSRID = PQgetvalue(hResult, 0, 2);
2303 27 : poSRS.reset(new OGRSpatialReference());
2304 27 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2305 :
2306 : // Try to import first from EPSG code, and then from WKT
2307 27 : if (pszAuthName && pszAuthSRID && EQUAL(pszAuthName, "EPSG") &&
2308 78 : atoi(pszAuthSRID) == nId &&
2309 24 : poSRS->importFromEPSG(nId) == OGRERR_NONE)
2310 : {
2311 : // do nothing
2312 : }
2313 3 : else if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
2314 : {
2315 0 : poSRS.reset();
2316 : }
2317 : }
2318 : else
2319 : {
2320 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not fetch SRS: %s",
2321 0 : PQerrorMessage(hPGConn));
2322 : }
2323 :
2324 27 : OGRPGClearResult(hResult);
2325 :
2326 27 : if (poSRS)
2327 27 : poSRS->StripTOWGS84IfKnownDatumAndAllowed();
2328 :
2329 : /* -------------------------------------------------------------------- */
2330 : /* Add to the cache. */
2331 : /* -------------------------------------------------------------------- */
2332 27 : oIter = m_oSRSCache.emplace(nId, std::move(poSRS)).first;
2333 27 : return oIter->second.get();
2334 : }
2335 :
2336 : /************************************************************************/
2337 : /* FetchSRSId() */
2338 : /* */
2339 : /* Fetch the id corresponding to an SRS, and if not found, add */
2340 : /* it to the table. */
2341 : /************************************************************************/
2342 :
2343 18 : int OGRPGDataSource::FetchSRSId(const OGRSpatialReference *poSRS)
2344 :
2345 : {
2346 18 : if (poSRS == nullptr || !m_bHasSpatialRefSys)
2347 0 : return nUndefinedSRID;
2348 :
2349 36 : OGRSpatialReference oSRS(*poSRS);
2350 : // cppcheck-suppress uselessAssignmentPtrArg
2351 18 : poSRS = nullptr;
2352 :
2353 18 : const char *pszAuthorityName = oSRS.GetAuthorityName(nullptr);
2354 :
2355 18 : if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
2356 : {
2357 : /* --------------------------------------------------------------------
2358 : */
2359 : /* Try to identify an EPSG code */
2360 : /* --------------------------------------------------------------------
2361 : */
2362 2 : oSRS.AutoIdentifyEPSG();
2363 :
2364 2 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
2365 2 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
2366 : {
2367 1 : const char *pszAuthorityCode = oSRS.GetAuthorityCode(nullptr);
2368 1 : if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
2369 : {
2370 : /* Import 'clean' SRS */
2371 1 : oSRS.importFromEPSG(atoi(pszAuthorityCode));
2372 :
2373 1 : pszAuthorityName = oSRS.GetAuthorityName(nullptr);
2374 : }
2375 : }
2376 : }
2377 : /* -------------------------------------------------------------------- */
2378 : /* Check whether the authority name/code is already mapped to a */
2379 : /* SRS ID. */
2380 : /* -------------------------------------------------------------------- */
2381 36 : CPLString osCommand;
2382 18 : int nAuthorityCode = 0;
2383 18 : if (pszAuthorityName != nullptr)
2384 : {
2385 : /* Check that the authority code is integral */
2386 17 : nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
2387 17 : if (nAuthorityCode > 0)
2388 : {
2389 : osCommand.Printf("SELECT srid FROM spatial_ref_sys WHERE "
2390 : "auth_name = '%s' AND auth_srid = %d",
2391 17 : pszAuthorityName, nAuthorityCode);
2392 17 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2393 :
2394 34 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
2395 17 : PQntuples(hResult) > 0)
2396 : {
2397 14 : int nSRSId = atoi(PQgetvalue(hResult, 0, 0));
2398 :
2399 14 : OGRPGClearResult(hResult);
2400 :
2401 14 : return nSRSId;
2402 : }
2403 :
2404 3 : OGRPGClearResult(hResult);
2405 : }
2406 : }
2407 :
2408 : /* -------------------------------------------------------------------- */
2409 : /* Translate SRS to WKT. */
2410 : /* -------------------------------------------------------------------- */
2411 4 : char *pszWKT = nullptr;
2412 4 : if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE)
2413 : {
2414 0 : CPLFree(pszWKT);
2415 0 : return nUndefinedSRID;
2416 : }
2417 :
2418 : /* -------------------------------------------------------------------- */
2419 : /* Try to find in the existing table. */
2420 : /* -------------------------------------------------------------------- */
2421 : CPLString osWKT =
2422 8 : OGRPGEscapeString(hPGConn, pszWKT, -1, "spatial_ref_sys", "srtext");
2423 : osCommand.Printf("SELECT srid FROM spatial_ref_sys WHERE srtext = %s",
2424 4 : osWKT.c_str());
2425 4 : PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2426 4 : CPLFree(pszWKT); // CM: Added to prevent mem leaks
2427 4 : pszWKT = nullptr; // CM: Added
2428 :
2429 : /* -------------------------------------------------------------------- */
2430 : /* We got it! Return it. */
2431 : /* -------------------------------------------------------------------- */
2432 8 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
2433 4 : PQntuples(hResult) > 0)
2434 : {
2435 0 : int nSRSId = atoi(PQgetvalue(hResult, 0, 0));
2436 :
2437 0 : OGRPGClearResult(hResult);
2438 :
2439 0 : return nSRSId;
2440 : }
2441 :
2442 : /* -------------------------------------------------------------------- */
2443 : /* If the command actually failed, then the metadata table is */
2444 : /* likely missing. Try defining it. */
2445 : /* -------------------------------------------------------------------- */
2446 : const bool bTableMissing =
2447 4 : hResult == nullptr || PQresultStatus(hResult) == PGRES_NONFATAL_ERROR;
2448 :
2449 4 : OGRPGClearResult(hResult);
2450 :
2451 4 : if (bTableMissing)
2452 : {
2453 0 : if (InitializeMetadataTables() != OGRERR_NONE)
2454 0 : return nUndefinedSRID;
2455 : }
2456 :
2457 : /* -------------------------------------------------------------------- */
2458 : /* Get the current maximum srid in the srs table. */
2459 : /* -------------------------------------------------------------------- */
2460 4 : hResult = OGRPG_PQexec(hPGConn, "SELECT MAX(srid) FROM spatial_ref_sys");
2461 :
2462 4 : int nSRSId = 1;
2463 4 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
2464 : {
2465 4 : nSRSId = atoi(PQgetvalue(hResult, 0, 0)) + 1;
2466 4 : OGRPGClearResult(hResult);
2467 : }
2468 :
2469 : /* -------------------------------------------------------------------- */
2470 : /* Try adding the SRS to the SRS table. */
2471 : /* -------------------------------------------------------------------- */
2472 4 : char *pszProj4 = nullptr;
2473 4 : if (oSRS.exportToProj4(&pszProj4) != OGRERR_NONE)
2474 : {
2475 0 : CPLFree(pszProj4);
2476 0 : return nUndefinedSRID;
2477 : }
2478 :
2479 4 : CPLString osProj4 = OGRPGEscapeString(hPGConn, pszProj4, -1,
2480 4 : "spatial_ref_sys", "proj4text");
2481 :
2482 4 : if (pszAuthorityName != nullptr && nAuthorityCode > 0)
2483 : {
2484 3 : nAuthorityCode = atoi(oSRS.GetAuthorityCode(nullptr));
2485 :
2486 3 : osCommand.Printf("INSERT INTO spatial_ref_sys "
2487 : "(srid,srtext,proj4text,auth_name,auth_srid) "
2488 : "VALUES (%d, %s, %s, '%s', %d)",
2489 : nSRSId, osWKT.c_str(), osProj4.c_str(),
2490 3 : pszAuthorityName, nAuthorityCode);
2491 : }
2492 : else
2493 : {
2494 : osCommand.Printf("INSERT INTO spatial_ref_sys (srid,srtext,proj4text) "
2495 : "VALUES (%d,%s,%s)",
2496 1 : nSRSId, osWKT.c_str(), osProj4.c_str());
2497 : }
2498 :
2499 : // Free everything that was allocated.
2500 4 : CPLFree(pszProj4);
2501 4 : CPLFree(pszWKT);
2502 :
2503 4 : hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
2504 4 : OGRPGClearResult(hResult);
2505 :
2506 4 : return nSRSId;
2507 : }
2508 :
2509 : /************************************************************************/
2510 : /* StartTransaction() */
2511 : /* */
2512 : /* Should only be called by user code. Not driver internals. */
2513 : /************************************************************************/
2514 :
2515 74 : OGRErr OGRPGDataSource::StartTransaction(CPL_UNUSED int bForce)
2516 : {
2517 74 : if (bUserTransactionActive)
2518 : {
2519 2 : CPLError(CE_Failure, CPLE_AppDefined,
2520 : "Transaction already established");
2521 2 : return OGRERR_FAILURE;
2522 : }
2523 :
2524 72 : CPLAssert(!bSavePointActive);
2525 72 : EndCopy();
2526 :
2527 72 : if (nSoftTransactionLevel == 0)
2528 : {
2529 66 : OGRErr eErr = DoTransactionCommand("BEGIN");
2530 66 : if (eErr != OGRERR_NONE)
2531 0 : return eErr;
2532 : }
2533 : else
2534 : {
2535 6 : OGRErr eErr = DoTransactionCommand("SAVEPOINT ogr_savepoint");
2536 6 : if (eErr != OGRERR_NONE)
2537 0 : return eErr;
2538 :
2539 6 : bSavePointActive = TRUE;
2540 : }
2541 :
2542 72 : nSoftTransactionLevel++;
2543 72 : bUserTransactionActive = true;
2544 :
2545 : /*CPLDebug("PG", "poDS=%p StartTransaction() nSoftTransactionLevel=%d",
2546 : this, nSoftTransactionLevel);*/
2547 :
2548 72 : return OGRERR_NONE;
2549 : }
2550 :
2551 : /************************************************************************/
2552 : /* CommitTransaction() */
2553 : /* */
2554 : /* Should only be called by user code. Not driver internals. */
2555 : /************************************************************************/
2556 :
2557 58 : OGRErr OGRPGDataSource::CommitTransaction()
2558 : {
2559 58 : if (!bUserTransactionActive)
2560 : {
2561 2 : CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
2562 2 : return OGRERR_FAILURE;
2563 : }
2564 :
2565 : /*CPLDebug("PG", "poDS=%p CommitTransaction() nSoftTransactionLevel=%d",
2566 : this, nSoftTransactionLevel);*/
2567 :
2568 56 : OGRErr eErr = FlushCacheWithRet(false);
2569 56 : if (eErr != OGRERR_NONE)
2570 : {
2571 1 : RollbackTransaction();
2572 1 : return eErr;
2573 : }
2574 :
2575 55 : nSoftTransactionLevel--;
2576 55 : bUserTransactionActive = false;
2577 :
2578 55 : if (bSavePointActive)
2579 : {
2580 4 : CPLAssert(nSoftTransactionLevel > 0);
2581 4 : bSavePointActive = FALSE;
2582 :
2583 4 : eErr = DoTransactionCommand("RELEASE SAVEPOINT ogr_savepoint");
2584 : }
2585 : else
2586 : {
2587 51 : if (nSoftTransactionLevel > 0)
2588 : {
2589 : // This means we have cursors still in progress
2590 7 : for (int i = 0; i < nLayers; i++)
2591 5 : papoLayers[i]->InvalidateCursor();
2592 2 : CPLAssert(nSoftTransactionLevel == 0);
2593 : }
2594 :
2595 51 : eErr = DoTransactionCommand("COMMIT");
2596 : }
2597 :
2598 55 : return eErr;
2599 : }
2600 :
2601 : /************************************************************************/
2602 : /* RollbackTransaction() */
2603 : /* */
2604 : /* Should only be called by user code. Not driver internals. */
2605 : /************************************************************************/
2606 :
2607 19 : OGRErr OGRPGDataSource::RollbackTransaction()
2608 : {
2609 19 : if (!bUserTransactionActive)
2610 : {
2611 2 : CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
2612 2 : return OGRERR_FAILURE;
2613 : }
2614 :
2615 : /*CPLDebug("PG", "poDS=%p RollbackTransaction() nSoftTransactionLevel=%d",
2616 : this, nSoftTransactionLevel);*/
2617 :
2618 17 : FlushCache(false);
2619 :
2620 17 : nSoftTransactionLevel--;
2621 17 : bUserTransactionActive = false;
2622 :
2623 : OGRErr eErr;
2624 17 : if (bSavePointActive)
2625 : {
2626 2 : CPLAssert(nSoftTransactionLevel > 0);
2627 2 : bSavePointActive = FALSE;
2628 :
2629 2 : eErr = DoTransactionCommand("ROLLBACK TO SAVEPOINT ogr_savepoint");
2630 : }
2631 : else
2632 : {
2633 15 : if (nSoftTransactionLevel > 0)
2634 : {
2635 : // This means we have cursors still in progress
2636 0 : for (int i = 0; i < nLayers; i++)
2637 0 : papoLayers[i]->InvalidateCursor();
2638 0 : CPLAssert(nSoftTransactionLevel == 0);
2639 : }
2640 :
2641 15 : eErr = DoTransactionCommand("ROLLBACK");
2642 : }
2643 :
2644 17 : return eErr;
2645 : }
2646 :
2647 : /************************************************************************/
2648 : /* SoftStartTransaction() */
2649 : /* */
2650 : /* Create a transaction scope. If we already have a */
2651 : /* transaction active this isn't a real transaction, but just */
2652 : /* an increment to the scope count. */
2653 : /************************************************************************/
2654 :
2655 908 : OGRErr OGRPGDataSource::SoftStartTransaction()
2656 :
2657 : {
2658 908 : nSoftTransactionLevel++;
2659 : /*CPLDebug("PG", "poDS=%p SoftStartTransaction() nSoftTransactionLevel=%d",
2660 : this, nSoftTransactionLevel);*/
2661 :
2662 908 : OGRErr eErr = OGRERR_NONE;
2663 908 : if (nSoftTransactionLevel == 1)
2664 : {
2665 823 : eErr = DoTransactionCommand("BEGIN");
2666 : }
2667 :
2668 908 : return eErr;
2669 : }
2670 :
2671 : /************************************************************************/
2672 : /* SoftCommitTransaction() */
2673 : /* */
2674 : /* Commit the current transaction if we are at the outer */
2675 : /* scope. */
2676 : /************************************************************************/
2677 :
2678 906 : OGRErr OGRPGDataSource::SoftCommitTransaction()
2679 :
2680 : {
2681 906 : EndCopy();
2682 :
2683 : /*CPLDebug("PG", "poDS=%p SoftCommitTransaction() nSoftTransactionLevel=%d",
2684 : this, nSoftTransactionLevel);*/
2685 :
2686 906 : if (nSoftTransactionLevel <= 0)
2687 : {
2688 0 : CPLAssert(false);
2689 : return OGRERR_FAILURE;
2690 : }
2691 :
2692 906 : OGRErr eErr = OGRERR_NONE;
2693 906 : nSoftTransactionLevel--;
2694 906 : if (nSoftTransactionLevel == 0)
2695 : {
2696 823 : CPLAssert(!bSavePointActive);
2697 :
2698 823 : eErr = DoTransactionCommand("COMMIT");
2699 : }
2700 :
2701 906 : return eErr;
2702 : }
2703 :
2704 : /************************************************************************/
2705 : /* SoftRollbackTransaction() */
2706 : /* */
2707 : /* Do a rollback of the current transaction if we are at the 1st */
2708 : /* level */
2709 : /************************************************************************/
2710 :
2711 2 : OGRErr OGRPGDataSource::SoftRollbackTransaction()
2712 :
2713 : {
2714 2 : EndCopy();
2715 :
2716 : /*CPLDebug("PG", "poDS=%p SoftRollbackTransaction()
2717 : nSoftTransactionLevel=%d", this, nSoftTransactionLevel);*/
2718 :
2719 2 : if (nSoftTransactionLevel <= 0)
2720 : {
2721 0 : CPLAssert(false);
2722 : return OGRERR_FAILURE;
2723 : }
2724 :
2725 2 : OGRErr eErr = OGRERR_NONE;
2726 2 : nSoftTransactionLevel--;
2727 2 : if (nSoftTransactionLevel == 0)
2728 : {
2729 2 : CPLAssert(!bSavePointActive);
2730 :
2731 2 : eErr = DoTransactionCommand("ROLLBACK");
2732 : }
2733 :
2734 2 : return eErr;
2735 : }
2736 :
2737 : /************************************************************************/
2738 : /* FlushSoftTransaction() */
2739 : /* */
2740 : /* Force the unwinding of any active transaction, and its */
2741 : /* commit. Should only be used by datasource destructor */
2742 : /************************************************************************/
2743 :
2744 408 : OGRErr OGRPGDataSource::FlushSoftTransaction()
2745 :
2746 : {
2747 : /*CPLDebug("PG", "poDS=%p FlushSoftTransaction() nSoftTransactionLevel=%d",
2748 : this, nSoftTransactionLevel);*/
2749 :
2750 408 : if (nSoftTransactionLevel <= 0)
2751 408 : return OGRERR_NONE;
2752 :
2753 0 : bSavePointActive = FALSE;
2754 :
2755 0 : CPLAssert(nSoftTransactionLevel == 1);
2756 0 : nSoftTransactionLevel = 0;
2757 0 : return DoTransactionCommand("COMMIT");
2758 : }
2759 :
2760 : /************************************************************************/
2761 : /* DoTransactionCommand() */
2762 : /************************************************************************/
2763 :
2764 1792 : OGRErr OGRPGDataSource::DoTransactionCommand(const char *pszCommand)
2765 :
2766 : {
2767 1792 : OGRErr eErr = OGRERR_NONE;
2768 1792 : PGconn *l_hPGConn = GetPGConn();
2769 :
2770 1792 : PGresult *hResult = OGRPG_PQexec(l_hPGConn, pszCommand);
2771 1792 : osDebugLastTransactionCommand = pszCommand;
2772 :
2773 1792 : if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
2774 : {
2775 0 : eErr = OGRERR_FAILURE;
2776 : }
2777 :
2778 1792 : OGRPGClearResult(hResult);
2779 :
2780 1792 : return eErr;
2781 : }
2782 :
2783 : /************************************************************************/
2784 : /* OGRPGNoResetResultLayer */
2785 : /************************************************************************/
2786 :
2787 : class OGRPGNoResetResultLayer final : public OGRPGLayer
2788 : {
2789 : public:
2790 : OGRPGNoResetResultLayer(OGRPGDataSource *poDSIn, PGresult *hResultIn);
2791 :
2792 : virtual ~OGRPGNoResetResultLayer();
2793 :
2794 : virtual void ResetReading() override;
2795 :
2796 0 : virtual int TestCapability(const char *) override
2797 : {
2798 0 : return FALSE;
2799 : }
2800 :
2801 : virtual OGRFeature *GetNextFeature() override;
2802 :
2803 0 : virtual CPLString GetFromClauseForGetExtent() override
2804 : {
2805 0 : CPLAssert(false);
2806 : return "";
2807 : }
2808 :
2809 47 : virtual void ResolveSRID(const OGRPGGeomFieldDefn *poGFldDefn) override
2810 : {
2811 47 : poGFldDefn->nSRSId = -1;
2812 47 : }
2813 : };
2814 :
2815 : /************************************************************************/
2816 : /* OGRPGNoResetResultLayer() */
2817 : /************************************************************************/
2818 :
2819 95 : OGRPGNoResetResultLayer::OGRPGNoResetResultLayer(OGRPGDataSource *poDSIn,
2820 95 : PGresult *hResultIn)
2821 : {
2822 95 : poDS = poDSIn;
2823 95 : ReadResultDefinition(hResultIn);
2824 95 : hCursorResult = hResultIn;
2825 95 : CreateMapFromFieldNameToIndex(hCursorResult, poFeatureDefn,
2826 95 : m_panMapFieldNameToIndex,
2827 95 : m_panMapFieldNameToGeomIndex);
2828 95 : }
2829 :
2830 : /************************************************************************/
2831 : /* ~OGRPGNoResetResultLayer() */
2832 : /************************************************************************/
2833 :
2834 380 : OGRPGNoResetResultLayer::~OGRPGNoResetResultLayer()
2835 :
2836 : {
2837 95 : OGRPGClearResult(hCursorResult);
2838 95 : hCursorResult = nullptr;
2839 190 : }
2840 :
2841 : /************************************************************************/
2842 : /* ResetReading() */
2843 : /************************************************************************/
2844 :
2845 95 : void OGRPGNoResetResultLayer::ResetReading()
2846 : {
2847 95 : iNextShapeId = 0;
2848 95 : }
2849 :
2850 : /************************************************************************/
2851 : /* GetNextFeature() */
2852 : /************************************************************************/
2853 :
2854 190 : OGRFeature *OGRPGNoResetResultLayer::GetNextFeature()
2855 :
2856 : {
2857 190 : if (iNextShapeId == PQntuples(hCursorResult))
2858 : {
2859 95 : return nullptr;
2860 : }
2861 190 : return RecordToFeature(hCursorResult, m_panMapFieldNameToIndex,
2862 95 : m_panMapFieldNameToGeomIndex,
2863 95 : static_cast<int>(iNextShapeId++));
2864 : }
2865 :
2866 : /************************************************************************/
2867 : /* OGRPGMemLayerWrapper */
2868 : /************************************************************************/
2869 :
2870 : class OGRPGMemLayerWrapper final : public OGRLayer
2871 : {
2872 : private:
2873 : OGRPGMemLayerWrapper(const OGRPGMemLayerWrapper &) = delete;
2874 : OGRPGMemLayerWrapper &operator=(const OGRPGMemLayerWrapper &) = delete;
2875 :
2876 : GDALDataset *poMemDS = nullptr;
2877 : OGRLayer *poMemLayer = nullptr;
2878 :
2879 : public:
2880 95 : explicit OGRPGMemLayerWrapper(GDALDataset *poMemDSIn)
2881 95 : {
2882 95 : poMemDS = poMemDSIn;
2883 95 : poMemLayer = poMemDS->GetLayer(0);
2884 95 : }
2885 :
2886 188 : virtual ~OGRPGMemLayerWrapper()
2887 94 : {
2888 94 : delete poMemDS;
2889 188 : }
2890 :
2891 0 : virtual void ResetReading() override
2892 : {
2893 0 : poMemLayer->ResetReading();
2894 0 : }
2895 :
2896 79 : virtual OGRFeature *GetNextFeature() override
2897 : {
2898 79 : return poMemLayer->GetNextFeature();
2899 : }
2900 :
2901 0 : virtual OGRFeatureDefn *GetLayerDefn() override
2902 : {
2903 0 : return poMemLayer->GetLayerDefn();
2904 : }
2905 :
2906 0 : virtual int TestCapability(const char *) override
2907 : {
2908 0 : return FALSE;
2909 : }
2910 : };
2911 :
2912 : /************************************************************************/
2913 : /* GetMetadataItem() */
2914 : /************************************************************************/
2915 :
2916 509 : const char *OGRPGDataSource::GetMetadataItem(const char *pszKey,
2917 : const char *pszDomain)
2918 : {
2919 : /* Only used by ogr_pg.py to check inner working */
2920 509 : if (pszDomain != nullptr && EQUAL(pszDomain, "_debug_") &&
2921 : pszKey != nullptr)
2922 : {
2923 278 : if (EQUAL(pszKey, "bHasLoadTables"))
2924 10 : return CPLSPrintf("%d", bHasLoadTables);
2925 268 : if (EQUAL(pszKey, "nSoftTransactionLevel"))
2926 70 : return CPLSPrintf("%d", nSoftTransactionLevel);
2927 198 : if (EQUAL(pszKey, "bSavePointActive"))
2928 66 : return CPLSPrintf("%d", bSavePointActive);
2929 132 : if (EQUAL(pszKey, "bUserTransactionActive"))
2930 66 : return CPLSPrintf("%d", bUserTransactionActive);
2931 66 : if (EQUAL(pszKey, "osDebugLastTransactionCommand"))
2932 : {
2933 : const char *pszRet =
2934 66 : CPLSPrintf("%s", osDebugLastTransactionCommand.c_str());
2935 66 : osDebugLastTransactionCommand = "";
2936 66 : return pszRet;
2937 : }
2938 : }
2939 231 : return GDALDataset::GetMetadataItem(pszKey, pszDomain);
2940 : }
2941 :
2942 : /************************************************************************/
2943 : /* ExecuteSQL() */
2944 : /************************************************************************/
2945 :
2946 1239 : OGRLayer *OGRPGDataSource::ExecuteSQL(const char *pszSQLCommand,
2947 : OGRGeometry *poSpatialFilter,
2948 : const char *pszDialect)
2949 :
2950 : {
2951 : /* Skip leading whitespace characters */
2952 1239 : while (std::isspace(static_cast<unsigned char>(*pszSQLCommand)))
2953 0 : pszSQLCommand++;
2954 :
2955 1239 : FlushCache(false);
2956 :
2957 : /* -------------------------------------------------------------------- */
2958 : /* Use generic implementation for recognized dialects */
2959 : /* -------------------------------------------------------------------- */
2960 1239 : if (IsGenericSQLDialect(pszDialect))
2961 0 : return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
2962 0 : pszDialect);
2963 :
2964 : /* -------------------------------------------------------------------- */
2965 : /* Special case DELLAYER: command. */
2966 : /* -------------------------------------------------------------------- */
2967 1239 : if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
2968 : {
2969 6 : const char *pszLayerName = pszSQLCommand + 9;
2970 :
2971 6 : while (*pszLayerName == ' ')
2972 0 : pszLayerName++;
2973 :
2974 6 : GetLayerCount();
2975 8 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
2976 : {
2977 6 : if (EQUAL(papoLayers[iLayer]->GetName(), pszLayerName))
2978 : {
2979 4 : DeleteLayer(iLayer);
2980 4 : break;
2981 : }
2982 : }
2983 6 : return nullptr;
2984 : }
2985 :
2986 : /* -------------------------------------------------------------------- */
2987 : /* Execute the statement. */
2988 : /* -------------------------------------------------------------------- */
2989 1233 : PGresult *hResult = nullptr;
2990 :
2991 1233 : if (STARTS_WITH_CI(pszSQLCommand, "SELECT") == FALSE ||
2992 211 : (strstr(pszSQLCommand, "from") == nullptr &&
2993 198 : strstr(pszSQLCommand, "FROM") == nullptr))
2994 : {
2995 : /* For something that is not a select or a select without table, do not
2996 : */
2997 : /* run under transaction (CREATE DATABASE, VACUUM don't like
2998 : * transactions) */
2999 :
3000 1112 : hResult =
3001 1112 : OGRPG_PQexec(hPGConn, pszSQLCommand, TRUE /* multiple allowed */);
3002 1112 : if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
3003 : {
3004 95 : CPLDebug("PG", "Command Results Tuples = %d", PQntuples(hResult));
3005 :
3006 : GDALDriver *poMemDriver =
3007 95 : GetGDALDriverManager()->GetDriverByName("Memory");
3008 95 : if (poMemDriver)
3009 : {
3010 : OGRPGLayer *poResultLayer =
3011 95 : new OGRPGNoResetResultLayer(this, hResult);
3012 : GDALDataset *poMemDS =
3013 95 : poMemDriver->Create("", 0, 0, 0, GDT_Unknown, nullptr);
3014 95 : poMemDS->CopyLayer(poResultLayer, "sql_statement");
3015 : OGRPGMemLayerWrapper *poResLayer =
3016 95 : new OGRPGMemLayerWrapper(poMemDS);
3017 95 : delete poResultLayer;
3018 95 : return poResLayer;
3019 : }
3020 : else
3021 0 : return nullptr;
3022 1017 : }
3023 : }
3024 : else
3025 : {
3026 121 : SoftStartTransaction();
3027 :
3028 121 : CPLString osCommand;
3029 : osCommand.Printf("DECLARE %s CURSOR for %s", "executeSQLCursor",
3030 121 : pszSQLCommand);
3031 :
3032 121 : hResult = OGRPG_PQexec(hPGConn, osCommand);
3033 :
3034 : /* --------------------------------------------------------------------
3035 : */
3036 : /* Do we have a tuple result? If so, instantiate a results */
3037 : /* layer for it. */
3038 : /* --------------------------------------------------------------------
3039 : */
3040 121 : if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
3041 : {
3042 120 : OGRPGClearResult(hResult);
3043 :
3044 120 : osCommand.Printf("FETCH 0 in %s", "executeSQLCursor");
3045 120 : hResult = OGRPG_PQexec(hPGConn, osCommand);
3046 :
3047 : OGRPGResultLayer *poLayer =
3048 120 : new OGRPGResultLayer(this, pszSQLCommand, hResult);
3049 :
3050 120 : OGRPGClearResult(hResult);
3051 :
3052 120 : osCommand.Printf("CLOSE %s", "executeSQLCursor");
3053 120 : hResult = OGRPG_PQexec(hPGConn, osCommand);
3054 120 : OGRPGClearResult(hResult);
3055 :
3056 120 : SoftCommitTransaction();
3057 :
3058 120 : if (poSpatialFilter != nullptr)
3059 5 : poLayer->SetSpatialFilter(poSpatialFilter);
3060 :
3061 120 : return poLayer;
3062 : }
3063 : else
3064 : {
3065 1 : SoftRollbackTransaction();
3066 : }
3067 : }
3068 :
3069 1018 : OGRPGClearResult(hResult);
3070 :
3071 1018 : return nullptr;
3072 : }
3073 :
3074 : /************************************************************************/
3075 : /* AbortSQL() */
3076 : /************************************************************************/
3077 :
3078 5 : OGRErr OGRPGDataSource::AbortSQL()
3079 : {
3080 5 : auto cancel = PQgetCancel(hPGConn);
3081 : int result;
3082 5 : if (cancel)
3083 : {
3084 : char errbuf[255];
3085 5 : result = PQcancel(cancel, errbuf, 255);
3086 5 : if (!result)
3087 0 : CPLDebug("PG", "Error canceling the query: %s", errbuf);
3088 5 : PQfreeCancel(cancel);
3089 5 : return result ? OGRERR_NONE : OGRERR_FAILURE;
3090 : }
3091 0 : return OGRERR_FAILURE;
3092 : }
3093 :
3094 : /************************************************************************/
3095 : /* ReleaseResultSet() */
3096 : /************************************************************************/
3097 :
3098 217 : void OGRPGDataSource::ReleaseResultSet(OGRLayer *poLayer)
3099 :
3100 : {
3101 217 : delete poLayer;
3102 217 : }
3103 :
3104 : /************************************************************************/
3105 : /* StartCopy() */
3106 : /************************************************************************/
3107 :
3108 3739 : void OGRPGDataSource::StartCopy(OGRPGTableLayer *poPGLayer)
3109 : {
3110 3739 : if (poLayerInCopyMode == poPGLayer)
3111 3554 : return;
3112 185 : EndCopy();
3113 185 : poLayerInCopyMode = poPGLayer;
3114 185 : poLayerInCopyMode->StartCopy();
3115 : }
3116 :
3117 : /************************************************************************/
3118 : /* EndCopy() */
3119 : /************************************************************************/
3120 :
3121 9580 : OGRErr OGRPGDataSource::EndCopy()
3122 : {
3123 9580 : if (poLayerInCopyMode != nullptr)
3124 : {
3125 185 : OGRErr result = poLayerInCopyMode->EndCopy();
3126 185 : poLayerInCopyMode = nullptr;
3127 :
3128 185 : return result;
3129 : }
3130 : else
3131 9395 : return OGRERR_NONE;
3132 : }
3133 :
3134 : /************************************************************************/
3135 : /* CreateMetadataTableIfNeeded() */
3136 : /************************************************************************/
3137 :
3138 4 : bool OGRPGDataSource::CreateMetadataTableIfNeeded()
3139 : {
3140 4 : if (m_bCreateMetadataTableIfNeededRun)
3141 0 : return m_bCreateMetadataTableIfNeededSuccess;
3142 :
3143 4 : m_bCreateMetadataTableIfNeededRun = true;
3144 :
3145 4 : const bool bIsSuperUser = IsSuperUser();
3146 4 : if (!bIsSuperUser && !OGRSystemTablesEventTriggerExists())
3147 : {
3148 1 : CPLError(CE_Warning, CPLE_AppDefined,
3149 : "User lacks super user privilege to be able to create event "
3150 : "trigger ogr_system_tables_event_trigger_for_metadata");
3151 1 : m_bCreateMetadataTableIfNeededSuccess = true;
3152 1 : return true;
3153 : }
3154 :
3155 : PGresult *hResult;
3156 :
3157 3 : hResult = OGRPG_PQexec(
3158 : hPGConn,
3159 : "SELECT c.oid FROM pg_class c "
3160 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
3161 : "WHERE c.relname = 'metadata' AND n.nspname = 'ogr_system_tables'");
3162 : const bool bFound =
3163 3 : (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0));
3164 3 : OGRPGClearResult(hResult);
3165 :
3166 3 : hResult = OGRPG_PQexec(
3167 : hPGConn,
3168 : "SELECT has_database_privilege((select current_database()), 'CREATE')");
3169 : const bool bCanCreateSchema =
3170 6 : (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0) &&
3171 3 : strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
3172 3 : OGRPGClearResult(hResult);
3173 :
3174 3 : if (!bFound)
3175 : {
3176 2 : if (!bCanCreateSchema)
3177 : {
3178 0 : CPLError(CE_Warning, CPLE_AppDefined,
3179 : "User lacks CREATE SCHEMA privilege to be able to create "
3180 : "ogr_system_tables.metadata table");
3181 0 : return false;
3182 : }
3183 : }
3184 : else
3185 : {
3186 1 : if (!HasWritePermissionsOnMetadataTable())
3187 : {
3188 0 : return false;
3189 : }
3190 1 : if (!bCanCreateSchema)
3191 : {
3192 0 : CPLError(CE_Warning, CPLE_AppDefined,
3193 : "User lacks CREATE SCHEMA privilege. Assuming "
3194 : "ogr_system_tables.metadata table has correct structure");
3195 0 : m_bCreateMetadataTableIfNeededSuccess = true;
3196 0 : return true;
3197 : }
3198 : }
3199 :
3200 3 : hResult =
3201 3 : OGRPG_PQexec(hPGConn, "CREATE SCHEMA IF NOT EXISTS ogr_system_tables");
3202 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3203 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3204 : {
3205 0 : OGRPGClearResult(hResult);
3206 0 : return false;
3207 : }
3208 3 : OGRPGClearResult(hResult);
3209 :
3210 3 : hResult = OGRPG_PQexec(
3211 : hPGConn, "CREATE TABLE IF NOT EXISTS ogr_system_tables.metadata("
3212 : "id SERIAL, "
3213 : "schema_name TEXT NOT NULL, "
3214 : "table_name TEXT NOT NULL, "
3215 : "metadata TEXT,"
3216 : "UNIQUE(schema_name, table_name))");
3217 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3218 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3219 : {
3220 0 : OGRPGClearResult(hResult);
3221 0 : return false;
3222 : }
3223 3 : OGRPGClearResult(hResult);
3224 :
3225 3 : hResult = OGRPG_PQexec(
3226 : hPGConn,
3227 : "DROP FUNCTION IF EXISTS "
3228 : "ogr_system_tables.event_trigger_function_for_metadata() CASCADE");
3229 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3230 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3231 : {
3232 0 : OGRPGClearResult(hResult);
3233 0 : return false;
3234 : }
3235 3 : OGRPGClearResult(hResult);
3236 :
3237 3 : hResult = OGRPG_PQexec(
3238 : hPGConn,
3239 : "CREATE FUNCTION "
3240 : "ogr_system_tables.event_trigger_function_for_metadata()\n"
3241 : "RETURNS event_trigger LANGUAGE plpgsql AS $$\n"
3242 : "DECLARE\n"
3243 : " obj record;\n"
3244 : "BEGIN\n"
3245 : " IF has_schema_privilege('ogr_system_tables', 'USAGE') THEN\n"
3246 : " IF has_table_privilege('ogr_system_tables.metadata', 'DELETE') "
3247 : "THEN\n"
3248 : " FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n"
3249 : " LOOP\n"
3250 : " IF obj.object_type = 'table' THEN\n"
3251 : " DELETE FROM ogr_system_tables.metadata m WHERE "
3252 : "m.schema_name = obj.schema_name AND m.table_name = "
3253 : "obj.object_name;\n"
3254 : " END IF;\n"
3255 : " END LOOP;\n"
3256 : " END IF;\n"
3257 : " END IF;\n"
3258 : "END;\n"
3259 : "$$;");
3260 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3261 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3262 : {
3263 0 : OGRPGClearResult(hResult);
3264 0 : return false;
3265 : }
3266 3 : OGRPGClearResult(hResult);
3267 :
3268 3 : if (bIsSuperUser)
3269 : {
3270 3 : hResult = OGRPG_PQexec(hPGConn,
3271 : "DROP EVENT TRIGGER IF EXISTS "
3272 : "ogr_system_tables_event_trigger_for_metadata");
3273 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3274 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3275 : {
3276 0 : OGRPGClearResult(hResult);
3277 0 : return false;
3278 : }
3279 3 : OGRPGClearResult(hResult);
3280 :
3281 3 : hResult = OGRPG_PQexec(
3282 : hPGConn,
3283 : "CREATE EVENT TRIGGER ogr_system_tables_event_trigger_for_metadata "
3284 : "ON sql_drop "
3285 : "EXECUTE FUNCTION "
3286 : "ogr_system_tables.event_trigger_function_for_metadata()");
3287 3 : if (!hResult || (PQresultStatus(hResult) != PGRES_COMMAND_OK &&
3288 0 : PQresultStatus(hResult) != PGRES_TUPLES_OK))
3289 : {
3290 0 : OGRPGClearResult(hResult);
3291 0 : return false;
3292 : }
3293 3 : OGRPGClearResult(hResult);
3294 : }
3295 :
3296 3 : m_bCreateMetadataTableIfNeededSuccess = true;
3297 3 : m_bOgrSystemTablesMetadataTableExistenceTested = true;
3298 3 : m_bOgrSystemTablesMetadataTableFound = true;
3299 3 : return true;
3300 : }
3301 :
3302 : /************************************************************************/
3303 : /* IsSuperUser() */
3304 : /************************************************************************/
3305 :
3306 4 : bool OGRPGDataSource::IsSuperUser()
3307 : {
3308 4 : PGresult *hResult = OGRPG_PQexec(
3309 4 : hPGConn, "SELECT usesuper FROM pg_user WHERE usename = CURRENT_USER");
3310 : const bool bRet =
3311 7 : (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0) &&
3312 3 : strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
3313 4 : OGRPGClearResult(hResult);
3314 4 : return bRet;
3315 : }
3316 :
3317 : /************************************************************************/
3318 : /* OGRSystemTablesEventTriggerExists() */
3319 : /************************************************************************/
3320 :
3321 1 : bool OGRPGDataSource::OGRSystemTablesEventTriggerExists()
3322 : {
3323 : PGresult *hResult =
3324 1 : OGRPG_PQexec(hPGConn, "SELECT 1 FROM pg_event_trigger WHERE evtname = "
3325 1 : "'ogr_system_tables_event_trigger_for_metadata'");
3326 1 : const bool bRet = (hResult && PQntuples(hResult) == 1);
3327 1 : OGRPGClearResult(hResult);
3328 1 : return bRet;
3329 : }
3330 :
3331 : /************************************************************************/
3332 : /* HasOgrSystemTablesMetadataTable() */
3333 : /************************************************************************/
3334 :
3335 60 : bool OGRPGDataSource::HasOgrSystemTablesMetadataTable()
3336 : {
3337 93 : if (!m_bOgrSystemTablesMetadataTableExistenceTested &&
3338 33 : CPLTestBool(CPLGetConfigOption("OGR_PG_ENABLE_METADATA", "YES")))
3339 : {
3340 30 : m_bOgrSystemTablesMetadataTableExistenceTested = true;
3341 : // Check that the ogr_system_tables.metadata table exists (without
3342 : // causing errors that might abort transactions)
3343 30 : PGresult *hResult = OGRPG_PQexec(
3344 : hPGConn,
3345 : "SELECT c.oid FROM pg_class c "
3346 : "JOIN pg_namespace n ON c.relnamespace=n.oid "
3347 30 : "WHERE c.relname = 'metadata' AND n.nspname = 'ogr_system_tables'");
3348 : const bool bFound =
3349 30 : (hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult, 0, 0));
3350 30 : OGRPGClearResult(hResult);
3351 30 : if (!bFound)
3352 14 : return false;
3353 :
3354 17 : hResult = OGRPG_PQexec(
3355 : hPGConn,
3356 : "SELECT has_schema_privilege('ogr_system_tables', 'USAGE')");
3357 : const bool bHasSchemaPrivilege =
3358 17 : (hResult && PQntuples(hResult) == 1 &&
3359 51 : !PQgetisnull(hResult, 0, 0) &&
3360 17 : strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
3361 17 : OGRPGClearResult(hResult);
3362 17 : if (!bHasSchemaPrivilege)
3363 : {
3364 1 : CPLError(CE_Warning, CPLE_AppDefined,
3365 : "Table ogr_system_tables.metadata exists but user lacks "
3366 : "USAGE privilege on ogr_system_tables schema");
3367 1 : return false;
3368 : }
3369 :
3370 16 : hResult = OGRPG_PQexec(
3371 : hPGConn, "SELECT has_table_privilege('ogr_system_tables.metadata', "
3372 : "'SELECT')");
3373 16 : m_bOgrSystemTablesMetadataTableFound =
3374 16 : (hResult && PQntuples(hResult) == 1 &&
3375 48 : !PQgetisnull(hResult, 0, 0) &&
3376 16 : strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
3377 16 : OGRPGClearResult(hResult);
3378 16 : if (!m_bOgrSystemTablesMetadataTableFound)
3379 : {
3380 0 : CPLError(CE_Warning, CPLE_AppDefined,
3381 : "Table ogr_system_tables.metadata exists but user lacks "
3382 : "SELECT privilege on it");
3383 : }
3384 : }
3385 46 : return m_bOgrSystemTablesMetadataTableFound;
3386 : }
3387 :
3388 : /************************************************************************/
3389 : /* HasWritePermissionsOnMetadataTable() */
3390 : /************************************************************************/
3391 :
3392 12 : bool OGRPGDataSource::HasWritePermissionsOnMetadataTable()
3393 : {
3394 12 : if (!m_bHasWritePermissionsOnMetadataTableRun)
3395 : {
3396 11 : m_bHasWritePermissionsOnMetadataTableRun = true;
3397 :
3398 11 : if (HasOgrSystemTablesMetadataTable())
3399 : {
3400 10 : PGresult *hResult = OGRPG_PQexec(
3401 : hPGConn,
3402 : "SELECT has_table_privilege('ogr_system_tables.metadata', "
3403 : "'INSERT') "
3404 : "AND has_table_privilege('ogr_system_tables.metadata', "
3405 10 : "'DELETE')");
3406 10 : m_bHasWritePermissionsOnMetadataTableSuccess =
3407 10 : (hResult && PQntuples(hResult) == 1 &&
3408 30 : !PQgetisnull(hResult, 0, 0) &&
3409 10 : strcmp(PQgetvalue(hResult, 0, 0), "t") == 0);
3410 10 : OGRPGClearResult(hResult);
3411 10 : if (!m_bHasWritePermissionsOnMetadataTableSuccess)
3412 : {
3413 0 : CPLError(CE_Warning, CPLE_AppDefined,
3414 : "User lacks INSERT and/OR DELETE privilege on "
3415 : "ogr_system_tables.metadata table");
3416 : }
3417 : }
3418 : }
3419 12 : return m_bHasWritePermissionsOnMetadataTableSuccess;
3420 : }
|