Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRPGDumpDataSource class.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include <algorithm>
14 : #include <cstring>
15 : #include "ogr_pgdump.h"
16 : #include "cpl_conv.h"
17 : #include "cpl_md5.h"
18 : #include "cpl_string.h"
19 :
20 : /************************************************************************/
21 : /* OGRPGDumpDataSource() */
22 : /************************************************************************/
23 :
24 103 : OGRPGDumpDataSource::OGRPGDumpDataSource(const char *pszNameIn,
25 103 : char **papszOptions)
26 : {
27 103 : SetDescription(pszNameIn);
28 :
29 103 : const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
30 :
31 103 : bool bUseCRLF = false;
32 103 : if (pszCRLFFormat == nullptr)
33 : {
34 : #ifdef _WIN32
35 : bUseCRLF = true;
36 : #endif
37 : }
38 58 : else if (EQUAL(pszCRLFFormat, "CRLF"))
39 : {
40 1 : bUseCRLF = true;
41 : }
42 57 : else if (EQUAL(pszCRLFFormat, "LF"))
43 : {
44 57 : bUseCRLF = false;
45 : }
46 : else
47 : {
48 0 : CPLError(CE_Warning, CPLE_AppDefined,
49 : "LINEFORMAT=%s not understood, use one of CRLF or LF.",
50 : pszCRLFFormat);
51 : #ifdef _WIN32
52 : bUseCRLF = true;
53 : #endif
54 : }
55 :
56 103 : if (bUseCRLF)
57 1 : m_pszEOL = "\r\n";
58 :
59 103 : m_fp = VSIFOpenL(pszNameIn, "wb");
60 103 : if (m_fp == nullptr)
61 : {
62 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszNameIn);
63 1 : return;
64 : }
65 : }
66 :
67 : /************************************************************************/
68 : /* ~OGRPGDumpDataSource() */
69 : /************************************************************************/
70 :
71 206 : OGRPGDumpDataSource::~OGRPGDumpDataSource()
72 :
73 : {
74 103 : EndCopy();
75 103 : m_apoLayers.clear();
76 :
77 103 : if (m_fp)
78 : {
79 102 : LogCommit();
80 102 : VSIFCloseL(m_fp);
81 102 : m_fp = nullptr;
82 : }
83 206 : }
84 :
85 : /************************************************************************/
86 : /* LogStartTransaction() */
87 : /************************************************************************/
88 :
89 118 : void OGRPGDumpDataSource::LogStartTransaction()
90 : {
91 118 : if (m_bInTransaction)
92 0 : return;
93 118 : m_bInTransaction = true;
94 118 : Log("BEGIN");
95 : }
96 :
97 : /************************************************************************/
98 : /* LogCommit() */
99 : /************************************************************************/
100 :
101 220 : void OGRPGDumpDataSource::LogCommit()
102 : {
103 220 : EndCopy();
104 :
105 220 : if (!m_bInTransaction)
106 102 : return;
107 118 : m_bInTransaction = false;
108 118 : Log("COMMIT");
109 : }
110 :
111 : /************************************************************************/
112 : /* OGRPGCommonLaunderName() */
113 : /************************************************************************/
114 :
115 212462 : char *OGRPGCommonLaunderName(const char *pszSrcName, const char *pszDebugPrefix,
116 : bool bUTF8ToASCII)
117 :
118 : {
119 212462 : char *pszSafeName = bUTF8ToASCII ? CPLUTF8ForceToASCII(pszSrcName, '_')
120 212456 : : CPLStrdup(pszSrcName);
121 :
122 212462 : size_t i = 0; // needed after loop
123 212462 : int iUTF8Char = 0;
124 8917960 : for (; pszSafeName[i] != '\0'; i++)
125 : {
126 8761120 : if (static_cast<unsigned char>(pszSafeName[i]) <= 127)
127 : {
128 8761000 : pszSafeName[i] = static_cast<char>(
129 8761000 : CPLTolower(static_cast<unsigned char>(pszSafeName[i])));
130 8761000 : if (pszSafeName[i] == '\'' || pszSafeName[i] == '-' ||
131 8761000 : pszSafeName[i] == '#')
132 : {
133 6 : pszSafeName[i] = '_';
134 : }
135 : }
136 :
137 : // Truncate string by making sure we don't cut in the
138 : // middle of a UTF-8 multibyte character
139 : // Continuation bytes of such characters are of the form
140 : // 10xxxxxx (0x80), whereas single-byte are 0xxxxxxx
141 : // and the start of a multi-byte is 11xxxxxx
142 8761120 : if ((pszSafeName[i] & 0xc0) != 0x80)
143 : {
144 8761060 : ++iUTF8Char;
145 8761060 : if (iUTF8Char == OGR_PG_NAMEDATALEN)
146 55626 : break;
147 : }
148 : }
149 :
150 212462 : if (iUTF8Char == OGR_PG_NAMEDATALEN && pszSafeName[i] != '\0')
151 : {
152 55626 : constexpr int FIRST_8_CHARS_OF_MD5 = 8;
153 55626 : iUTF8Char = 0;
154 3059480 : for (i = 0; pszSafeName[i]; ++i)
155 : {
156 3059480 : if ((pszSafeName[i] & 0xc0) != 0x80)
157 : {
158 3059430 : ++iUTF8Char;
159 3059430 : if (iUTF8Char == OGR_PG_NAMEDATALEN - FIRST_8_CHARS_OF_MD5 - 1)
160 55626 : break;
161 : }
162 : }
163 55626 : pszSafeName[i] = '_';
164 55626 : memcpy(pszSafeName + i + 1, CPLMD5String(pszSrcName),
165 : FIRST_8_CHARS_OF_MD5);
166 55626 : i += FIRST_8_CHARS_OF_MD5 + 1;
167 : }
168 :
169 212462 : pszSafeName[i] = '\0';
170 :
171 212462 : if (strcmp(pszSrcName, pszSafeName) != 0)
172 : {
173 188668 : if (CPLStrlenUTF8Ex(pszSafeName) < CPLStrlenUTF8Ex(pszSrcName))
174 : {
175 55626 : CPLError(CE_Warning, CPLE_AppDefined,
176 : "%s identifier truncated to %s", pszSrcName, pszSafeName);
177 : }
178 : else
179 : {
180 133042 : CPLDebug(pszDebugPrefix, "LaunderName('%s') -> '%s'", pszSrcName,
181 : pszSafeName);
182 : }
183 : }
184 :
185 212462 : return pszSafeName;
186 : }
187 :
188 : /************************************************************************/
189 : /* ICreateLayer() */
190 : /************************************************************************/
191 :
192 : OGRLayer *
193 118 : OGRPGDumpDataSource::ICreateLayer(const char *pszLayerName,
194 : const OGRGeomFieldDefn *poGeomFieldDefn,
195 : CSLConstList papszOptions)
196 :
197 : {
198 118 : if (STARTS_WITH(pszLayerName, "pg"))
199 : {
200 0 : CPLError(CE_Warning, CPLE_AppDefined,
201 : "The layer name should not begin by 'pg' as it is a reserved "
202 : "prefix");
203 : }
204 :
205 118 : auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
206 : const auto poSRS =
207 118 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
208 :
209 118 : const bool bCreateTable = CPLFetchBool(papszOptions, "CREATE_TABLE", true);
210 : const bool bCreateSchema =
211 118 : CPLFetchBool(papszOptions, "CREATE_SCHEMA", true);
212 : const char *pszDropTable =
213 118 : CSLFetchNameValueDef(papszOptions, "DROP_TABLE", "IF_EXISTS");
214 : const bool bSkipConflicts =
215 118 : CPLFetchBool(papszOptions, "SKIP_CONFLICTS", false);
216 118 : int nGeometryTypeFlags = 0;
217 :
218 118 : if (OGR_GT_HasZ(eType))
219 23 : nGeometryTypeFlags |= OGRGeometry::OGR_G_3D;
220 118 : if (OGR_GT_HasM(eType))
221 2 : nGeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
222 :
223 118 : int nForcedGeometryTypeFlags = -1;
224 118 : const char *pszDim = CSLFetchNameValue(papszOptions, "DIM");
225 118 : if (pszDim != nullptr)
226 : {
227 19 : if (EQUAL(pszDim, "XY") || EQUAL(pszDim, "2"))
228 : {
229 0 : nGeometryTypeFlags = 0;
230 0 : nForcedGeometryTypeFlags = nGeometryTypeFlags;
231 : }
232 19 : else if (EQUAL(pszDim, "XYZ") || EQUAL(pszDim, "3"))
233 : {
234 7 : nGeometryTypeFlags = OGRGeometry::OGR_G_3D;
235 7 : nForcedGeometryTypeFlags = nGeometryTypeFlags;
236 : }
237 12 : else if (EQUAL(pszDim, "XYM"))
238 : {
239 6 : nGeometryTypeFlags = OGRGeometry::OGR_G_MEASURED;
240 6 : nForcedGeometryTypeFlags = nGeometryTypeFlags;
241 : }
242 6 : else if (EQUAL(pszDim, "XYZM") || EQUAL(pszDim, "4"))
243 : {
244 6 : nGeometryTypeFlags =
245 : OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
246 6 : nForcedGeometryTypeFlags = nGeometryTypeFlags;
247 : }
248 : else
249 : {
250 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for DIM");
251 : }
252 : }
253 :
254 118 : const int nDimension =
255 118 : 2 + ((nGeometryTypeFlags & OGRGeometry::OGR_G_3D) ? 1 : 0) +
256 118 : ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) ? 1 : 0);
257 :
258 : /* Should we turn layers with None geometry type as Unknown/GEOMETRY */
259 : /* so they are still recorded in geometry_columns table ? (#4012) */
260 118 : const bool bNoneAsUnknown = CPLTestBool(
261 : CSLFetchNameValueDef(papszOptions, "NONE_AS_UNKNOWN", "NO"));
262 :
263 118 : if (bNoneAsUnknown && eType == wkbNone)
264 0 : eType = wkbUnknown;
265 :
266 118 : const bool bExtractSchemaFromLayerName = CPLTestBool(CSLFetchNameValueDef(
267 : papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
268 :
269 : // Postgres Schema handling:
270 :
271 : // Extract schema name from input layer name or passed with -lco SCHEMA.
272 : // Set layer name to "schema.table" or to "table" if schema ==
273 : // current_schema() Usage without schema name is backwards compatible
274 :
275 118 : const char *pszDotPos = strstr(pszLayerName, ".");
276 236 : std::string osTable;
277 236 : std::string osSchema;
278 : const bool bUTF8ToASCII =
279 118 : CPLFetchBool(papszOptions, "LAUNDER_ASCII", false);
280 : const bool bLaunder =
281 118 : bUTF8ToASCII || CPLFetchBool(papszOptions, "LAUNDER", true);
282 :
283 118 : if (pszDotPos != nullptr && bExtractSchemaFromLayerName)
284 : {
285 48 : const size_t length = static_cast<size_t>(pszDotPos - pszLayerName);
286 48 : osSchema = pszLayerName;
287 48 : osSchema.resize(length);
288 :
289 48 : if (bLaunder)
290 : {
291 48 : char *pszTmp = OGRPGCommonLaunderName(pszDotPos + 1, "PGDump",
292 : bUTF8ToASCII); // skip "."
293 48 : osTable = pszTmp;
294 48 : CPLFree(pszTmp);
295 : }
296 : else
297 0 : osTable = OGRPGCommonGenerateShortEnoughIdentifier(pszDotPos +
298 48 : 1); // skip "."
299 : }
300 : else
301 : {
302 70 : if (bLaunder)
303 : {
304 : char *pszTmp =
305 67 : OGRPGCommonLaunderName(pszLayerName, "PGDump", bUTF8ToASCII);
306 67 : osTable = pszTmp;
307 67 : CPLFree(pszTmp);
308 : }
309 : else
310 3 : osTable = OGRPGCommonGenerateShortEnoughIdentifier(pszLayerName);
311 : }
312 :
313 : const std::string osTableEscaped =
314 236 : OGRPGDumpEscapeColumnName(osTable.c_str());
315 118 : const char *pszTableEscaped = osTableEscaped.c_str();
316 :
317 118 : LogCommit();
318 :
319 : /* -------------------------------------------------------------------- */
320 : /* Set the default schema for the layers. */
321 : /* -------------------------------------------------------------------- */
322 236 : CPLString osCommand;
323 :
324 118 : const char *pszSchemaOption = CSLFetchNameValue(papszOptions, "SCHEMA");
325 118 : if (pszSchemaOption)
326 : {
327 2 : osSchema = pszSchemaOption;
328 2 : if (bCreateSchema)
329 : {
330 : osCommand.Printf(
331 : "CREATE SCHEMA %s",
332 2 : OGRPGDumpEscapeColumnName(osSchema.c_str()).c_str());
333 2 : Log(osCommand);
334 : }
335 : }
336 :
337 118 : const bool bTemporary = CPLFetchBool(papszOptions, "TEMPORARY", false);
338 118 : if (bTemporary)
339 : {
340 1 : osSchema = "pg_temp";
341 : }
342 :
343 118 : if (osSchema.empty())
344 : {
345 67 : osSchema = "public";
346 : }
347 : const std::string osSchemaEscaped =
348 236 : OGRPGDumpEscapeColumnName(osSchema.c_str());
349 118 : const char *pszSchemaEscaped = osSchemaEscaped.c_str();
350 :
351 : /* -------------------------------------------------------------------- */
352 : /* Do we already have this layer? */
353 : /* -------------------------------------------------------------------- */
354 134 : for (const auto &poLayer : m_apoLayers)
355 : {
356 16 : if (EQUAL(pszLayerName, poLayer->GetDescription()))
357 : {
358 0 : CPLError(CE_Failure, CPLE_AppDefined,
359 : "Layer %s already exists, CreateLayer failed.\n",
360 : pszLayerName);
361 0 : return nullptr;
362 : }
363 : }
364 :
365 118 : if (bCreateTable &&
366 117 : (EQUAL(pszDropTable, "YES") || EQUAL(pszDropTable, "ON") ||
367 117 : EQUAL(pszDropTable, "TRUE") || EQUAL(pszDropTable, "IF_EXISTS")))
368 : {
369 117 : if (EQUAL(pszDropTable, "IF_EXISTS"))
370 : osCommand.Printf("DROP TABLE IF EXISTS %s.%s CASCADE",
371 117 : pszSchemaEscaped, pszTableEscaped);
372 : else
373 : osCommand.Printf("DROP TABLE %s.%s CASCADE", pszSchemaEscaped,
374 0 : pszTableEscaped);
375 117 : Log(osCommand);
376 : }
377 :
378 : /* -------------------------------------------------------------------- */
379 : /* Handle the GEOM_TYPE option. */
380 : /* -------------------------------------------------------------------- */
381 118 : const char *pszGeomType = CSLFetchNameValue(papszOptions, "GEOM_TYPE");
382 118 : if (pszGeomType == nullptr)
383 : {
384 111 : pszGeomType = "geometry";
385 : }
386 :
387 118 : if (!EQUAL(pszGeomType, "geometry") && !EQUAL(pszGeomType, "geography"))
388 : {
389 0 : CPLError(
390 : CE_Failure, CPLE_AppDefined,
391 : "GEOM_TYPE in PostGIS enabled databases must be 'geometry' or "
392 : "'geography'. Creation of layer %s with GEOM_TYPE %s has failed.",
393 : pszLayerName, pszGeomType);
394 0 : return nullptr;
395 : }
396 :
397 : /* -------------------------------------------------------------------- */
398 : /* Try to get the SRS Id of this spatial reference system, */
399 : /* adding tot the srs table if needed. */
400 : /* -------------------------------------------------------------------- */
401 : const char *pszPostgisVersion =
402 118 : CSLFetchNameValueDef(papszOptions, "POSTGIS_VERSION", "2.2");
403 118 : const int nPostGISMajor = atoi(pszPostgisVersion);
404 118 : const char *pszPostgisVersionDot = strchr(pszPostgisVersion, '.');
405 118 : const int nPostGISMinor =
406 118 : pszPostgisVersionDot ? atoi(pszPostgisVersionDot + 1) : 0;
407 118 : const int nUnknownSRSId = nPostGISMajor >= 2 ? 0 : -1;
408 :
409 118 : int nSRSId = nUnknownSRSId;
410 118 : int nForcedSRSId = -2;
411 118 : const char *pszSRID = CSLFetchNameValue(papszOptions, "SRID");
412 118 : if (pszSRID)
413 : {
414 1 : nSRSId = atoi(pszSRID);
415 1 : nForcedSRSId = nSRSId;
416 : }
417 : else
418 : {
419 117 : if (poSRS)
420 : {
421 0 : const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr);
422 0 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
423 : {
424 : /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */
425 0 : nSRSId = atoi(poSRS->GetAuthorityCode(nullptr));
426 : }
427 : else
428 : {
429 0 : const char *pszGeogCSName = poSRS->GetAttrValue("GEOGCS");
430 0 : if (pszGeogCSName != nullptr &&
431 0 : EQUAL(pszGeogCSName, "GCS_WGS_1984"))
432 : {
433 0 : nSRSId = 4326;
434 : }
435 : }
436 : }
437 : }
438 :
439 : const std::string osEscapedTableNameSingleQuote =
440 236 : OGRPGDumpEscapeString(osTable.c_str());
441 : const char *pszEscapedTableNameSingleQuote =
442 118 : osEscapedTableNameSingleQuote.c_str();
443 :
444 118 : const char *pszGeometryType = OGRToOGCGeomType(eType);
445 :
446 118 : const char *pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
447 118 : if (eType != wkbNone && !EQUAL(pszGeomType, "geography"))
448 : {
449 81 : if (pszGFldName == nullptr)
450 73 : pszGFldName = "wkb_geometry";
451 :
452 81 : if (nPostGISMajor < 2)
453 : {
454 : // Sometimes there is an old cruft entry in the geometry_columns
455 : // table if things were not properly cleaned up before. We make
456 : // an effort to clean out such cruft.
457 : //
458 : // Note: PostGIS 2.0 defines geometry_columns as a view (no clean up
459 : // is needed).
460 :
461 : osCommand.Printf("DELETE FROM geometry_columns "
462 : "WHERE f_table_name = %s AND f_table_schema = %s",
463 : pszEscapedTableNameSingleQuote,
464 1 : OGRPGDumpEscapeString(osSchema.c_str()).c_str());
465 1 : if (bCreateTable)
466 1 : Log(osCommand);
467 : }
468 : }
469 :
470 118 : LogStartTransaction();
471 :
472 : /* -------------------------------------------------------------------- */
473 : /* Create an empty table first. */
474 : /* -------------------------------------------------------------------- */
475 118 : if (bCreateTable)
476 : {
477 117 : if (bTemporary)
478 : {
479 1 : osCommand.Printf("CREATE TEMPORARY TABLE %s()", pszTableEscaped);
480 : }
481 : else
482 : {
483 : osCommand.Printf("CREATE%s TABLE %s.%s()",
484 116 : CPLFetchBool(papszOptions, "UNLOGGED", false)
485 : ? " UNLOGGED"
486 : : "",
487 116 : pszSchemaEscaped, pszTableEscaped);
488 : }
489 117 : Log(osCommand);
490 : }
491 :
492 : /* -------------------------------------------------------------------- */
493 : /* Add FID if needed. */
494 : /* -------------------------------------------------------------------- */
495 118 : const char *pszFIDColumnNameIn = CSLFetchNameValue(papszOptions, "FID");
496 236 : CPLString osFIDColumnName;
497 118 : if (pszFIDColumnNameIn == nullptr)
498 112 : osFIDColumnName = "ogc_fid";
499 : else
500 : {
501 6 : if (bLaunder)
502 : {
503 6 : char *pszLaunderedFid = OGRPGCommonLaunderName(
504 : pszFIDColumnNameIn, "PGDump", bUTF8ToASCII);
505 6 : osFIDColumnName = pszLaunderedFid;
506 6 : CPLFree(pszLaunderedFid);
507 : }
508 : else
509 : {
510 0 : osFIDColumnName = pszFIDColumnNameIn;
511 : }
512 : }
513 : const CPLString osFIDColumnNameEscaped =
514 236 : OGRPGDumpEscapeColumnName(osFIDColumnName);
515 :
516 118 : const bool bFID64 = CPLFetchBool(papszOptions, "FID64", false);
517 118 : const char *pszSerialType = bFID64 ? "BIGSERIAL" : "SERIAL";
518 :
519 118 : if (bCreateTable && !osFIDColumnName.empty())
520 : {
521 230 : std::string osConstraintName(osTable);
522 115 : if (CPLStrlenUTF8Ex(osTable.c_str()) + strlen("_pk") >
523 : static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
524 : {
525 6 : osConstraintName.clear();
526 6 : size_t iUTF8Char = 0;
527 464 : for (size_t i = 0; i < osTable.size(); ++i)
528 : {
529 464 : if ((osTable[i] & 0xc0) != 0x80)
530 : {
531 366 : ++iUTF8Char;
532 366 : if (iUTF8Char == OGR_PG_NAMEDATALEN - strlen("_pk"))
533 6 : break;
534 : }
535 458 : osConstraintName += osTable[i];
536 : }
537 : }
538 115 : osConstraintName += "_pk";
539 : osCommand.Printf(
540 : "ALTER TABLE %s.%s ADD COLUMN %s %s "
541 : "CONSTRAINT %s PRIMARY KEY",
542 : pszSchemaEscaped, pszTableEscaped, osFIDColumnNameEscaped.c_str(),
543 : pszSerialType,
544 115 : OGRPGDumpEscapeColumnName(osConstraintName.c_str()).c_str());
545 115 : Log(osCommand);
546 : }
547 :
548 : /* -------------------------------------------------------------------- */
549 : /* Create geometry/geography column (actual creation possibly */
550 : /* deferred). */
551 : /* -------------------------------------------------------------------- */
552 236 : std::vector<std::string> aosGeomCommands;
553 118 : if (bCreateTable && eType != wkbNone && EQUAL(pszGeomType, "geography"))
554 : {
555 7 : if (CSLFetchNameValue(papszOptions, "GEOMETRY_NAME") != nullptr)
556 0 : pszGFldName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
557 : else
558 7 : pszGFldName = "the_geog";
559 :
560 7 : const char *suffix = "";
561 7 : if ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED) &&
562 4 : (nGeometryTypeFlags & OGRGeometry::OGR_G_3D))
563 : {
564 2 : suffix = "ZM";
565 : }
566 5 : else if ((nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
567 : {
568 2 : suffix = "M";
569 : }
570 3 : else if ((nGeometryTypeFlags & OGRGeometry::OGR_G_3D))
571 : {
572 2 : suffix = "Z";
573 : }
574 :
575 7 : if (nSRSId)
576 : osCommand.Printf("ALTER TABLE %s.%s "
577 : "ADD COLUMN %s geography(%s%s,%d)",
578 : pszSchemaEscaped, pszTableEscaped,
579 0 : OGRPGDumpEscapeColumnName(pszGFldName).c_str(),
580 0 : pszGeometryType, suffix, nSRSId);
581 : else
582 : osCommand.Printf("ALTER TABLE %s.%s "
583 : "ADD COLUMN %s geography(%s%s)",
584 : pszSchemaEscaped, pszTableEscaped,
585 14 : OGRPGDumpEscapeColumnName(pszGFldName).c_str(),
586 7 : pszGeometryType, suffix);
587 7 : aosGeomCommands.push_back(osCommand);
588 : }
589 111 : else if (bCreateTable && eType != wkbNone)
590 : {
591 80 : const char *suffix = "";
592 80 : if (nGeometryTypeFlags ==
593 83 : static_cast<int>(OGRGeometry::OGR_G_MEASURED) &&
594 3 : wkbFlatten(eType) != wkbUnknown)
595 : {
596 2 : suffix = "M";
597 : }
598 :
599 : osCommand.Printf(
600 : "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
601 160 : OGRPGDumpEscapeString(bTemporary ? "" : osSchema.c_str()).c_str(),
602 : pszEscapedTableNameSingleQuote,
603 160 : OGRPGDumpEscapeString(pszGFldName).c_str(), nSRSId, pszGeometryType,
604 160 : suffix, nDimension);
605 80 : aosGeomCommands.push_back(osCommand);
606 : }
607 :
608 : const char *pszSI =
609 118 : CSLFetchNameValueDef(papszOptions, "SPATIAL_INDEX", "GIST");
610 118 : const bool bCreateSpatialIndex =
611 0 : (EQUAL(pszSI, "GIST") || EQUAL(pszSI, "SPGIST") ||
612 118 : EQUAL(pszSI, "BRIN") || EQUAL(pszSI, "YES") || EQUAL(pszSI, "ON") ||
613 0 : EQUAL(pszSI, "TRUE"));
614 118 : if (!bCreateSpatialIndex && !EQUAL(pszSI, "NO") && !EQUAL(pszSI, "OFF") &&
615 0 : !EQUAL(pszSI, "FALSE") && !EQUAL(pszSI, "NONE"))
616 : {
617 0 : CPLError(CE_Warning, CPLE_NotSupported,
618 : "SPATIAL_INDEX=%s not supported", pszSI);
619 : }
620 236 : const char *pszSpatialIndexType = EQUAL(pszSI, "SPGIST") ? "SPGIST"
621 118 : : EQUAL(pszSI, "BRIN") ? "BRIN"
622 : : "GIST";
623 :
624 236 : std::vector<std::string> aosSpatialIndexCreationCommands;
625 118 : if (bCreateTable && bCreateSpatialIndex && pszGFldName && eType != wkbNone)
626 : {
627 : const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
628 174 : osTable.c_str(), pszGFldName, 0));
629 :
630 : /* --------------------------------------------------------------- */
631 : /* Create the spatial index. */
632 : /* --------------------------------------------------------------- */
633 : osCommand.Printf("CREATE INDEX %s "
634 : "ON %s.%s "
635 : "USING %s (%s)",
636 174 : OGRPGDumpEscapeColumnName(osIndexName.c_str()).c_str(),
637 : pszSchemaEscaped, pszTableEscaped, pszSpatialIndexType,
638 261 : OGRPGDumpEscapeColumnName(pszGFldName).c_str());
639 87 : aosSpatialIndexCreationCommands.push_back(std::move(osCommand));
640 : }
641 :
642 : /* -------------------------------------------------------------------- */
643 : /* Create the layer object. */
644 : /* -------------------------------------------------------------------- */
645 : const bool bWriteAsHex =
646 118 : !CPLFetchBool(papszOptions, "WRITE_EWKT_GEOM", false);
647 :
648 : auto poLayer = std::make_unique<OGRPGDumpLayer>(
649 118 : this, osSchema.c_str(), osTable.c_str(),
650 118 : !osFIDColumnName.empty() ? osFIDColumnName.c_str() : nullptr,
651 236 : bWriteAsHex, bCreateTable, bSkipConflicts);
652 118 : poLayer->SetLaunderFlag(bLaunder);
653 118 : poLayer->SetUTF8ToASCIIFlag(bUTF8ToASCII);
654 118 : poLayer->SetPrecisionFlag(CPLFetchBool(papszOptions, "PRECISION", true));
655 :
656 : const char *pszOverrideColumnTypes =
657 118 : CSLFetchNameValue(papszOptions, "COLUMN_TYPES");
658 118 : poLayer->SetOverrideColumnTypes(pszOverrideColumnTypes);
659 118 : poLayer->SetUnknownSRSId(nUnknownSRSId);
660 118 : poLayer->SetForcedSRSId(nForcedSRSId);
661 118 : poLayer->SetCreateSpatialIndex(bCreateSpatialIndex, pszSpatialIndexType);
662 118 : poLayer->SetPostGISVersion(nPostGISMajor, nPostGISMinor);
663 118 : poLayer->SetForcedGeometryTypeFlags(nForcedGeometryTypeFlags);
664 :
665 : // Log geometry field creation immediately or defer it, according to
666 : // GEOM_COLUMN_POSITION
667 118 : const bool bGeomColumnPositionImmediate = EQUAL(
668 : CSLFetchNameValueDef(papszOptions, "GEOM_COLUMN_POSITION", "IMMEDIATE"),
669 : "IMMEDIATE");
670 118 : poLayer->SetGeomColumnPositionImmediate(bGeomColumnPositionImmediate);
671 118 : if (bGeomColumnPositionImmediate)
672 : {
673 194 : for (const auto &osSQL : aosGeomCommands)
674 82 : Log(osSQL.c_str());
675 : }
676 : else
677 : {
678 6 : poLayer->SetDeferredGeomFieldCreationCommands(aosGeomCommands);
679 : }
680 118 : poLayer->SetSpatialIndexCreationCommands(aosSpatialIndexCreationCommands);
681 :
682 118 : const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
683 118 : if (pszDescription != nullptr)
684 1 : poLayer->SetForcedDescription(pszDescription);
685 :
686 118 : if (eType != wkbNone)
687 : {
688 176 : OGRGeomFieldDefn oTmp(pszGFldName, eType);
689 88 : auto poGeomField = std::make_unique<OGRPGDumpGeomFieldDefn>(&oTmp);
690 88 : poGeomField->m_nSRSId = nSRSId;
691 88 : poGeomField->m_nGeometryTypeFlags = nGeometryTypeFlags;
692 88 : poLayer->GetLayerDefn()->AddGeomFieldDefn(std::move(poGeomField));
693 : }
694 30 : else if (pszGFldName)
695 6 : poLayer->SetGeometryFieldName(pszGFldName);
696 :
697 : /* -------------------------------------------------------------------- */
698 : /* Add layer to data source layer list. */
699 : /* -------------------------------------------------------------------- */
700 118 : m_apoLayers.emplace_back(std::move(poLayer));
701 :
702 118 : return m_apoLayers.back().get();
703 : }
704 :
705 : /************************************************************************/
706 : /* TestCapability() */
707 : /************************************************************************/
708 :
709 75 : int OGRPGDumpDataSource::TestCapability(const char *pszCap)
710 :
711 : {
712 75 : if (EQUAL(pszCap, ODsCCreateLayer))
713 39 : return TRUE;
714 36 : else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
715 13 : return TRUE;
716 23 : else if (EQUAL(pszCap, ODsCCurveGeometries))
717 0 : return TRUE;
718 23 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
719 0 : return TRUE;
720 23 : else if (EQUAL(pszCap, ODsCZGeometries))
721 0 : return TRUE;
722 23 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
723 0 : return TRUE;
724 : else
725 23 : return FALSE;
726 : }
727 :
728 : /************************************************************************/
729 : /* GetLayer() */
730 : /************************************************************************/
731 :
732 7 : OGRLayer *OGRPGDumpDataSource::GetLayer(int iLayer)
733 :
734 : {
735 7 : if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
736 0 : return nullptr;
737 : else
738 7 : return m_apoLayers[iLayer].get();
739 : }
740 :
741 : /************************************************************************/
742 : /* Log() */
743 : /************************************************************************/
744 :
745 1274 : bool OGRPGDumpDataSource::Log(const char *pszStr, bool bAddSemiColumn)
746 : {
747 1274 : if (m_fp == nullptr)
748 : {
749 1 : return false;
750 : }
751 :
752 1273 : VSIFWriteL(pszStr, strlen(pszStr), 1, m_fp);
753 1273 : if (bAddSemiColumn)
754 : {
755 1232 : const char chSemiColumn = ';';
756 1232 : VSIFWriteL(&chSemiColumn, 1, 1, m_fp);
757 : }
758 1273 : VSIFWriteL(m_pszEOL, strlen(m_pszEOL), 1, m_fp);
759 1273 : return true;
760 : }
761 :
762 : /************************************************************************/
763 : /* StartCopy() */
764 : /************************************************************************/
765 10 : void OGRPGDumpDataSource::StartCopy(OGRPGDumpLayer *poPGLayer)
766 : {
767 10 : EndCopy();
768 10 : m_poLayerInCopyMode = poPGLayer;
769 10 : }
770 :
771 : /************************************************************************/
772 : /* EndCopy() */
773 : /************************************************************************/
774 333 : OGRErr OGRPGDumpDataSource::EndCopy()
775 : {
776 333 : if (m_poLayerInCopyMode != nullptr)
777 : {
778 10 : OGRErr result = m_poLayerInCopyMode->EndCopy();
779 10 : m_poLayerInCopyMode = nullptr;
780 :
781 10 : return result;
782 : }
783 :
784 323 : return OGRERR_NONE;
785 : }
|