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