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