1 : /******************************************************************************
2 : * $Id: ogrpgdumpdatasource.cpp 22821 2011-07-28 17:54:47Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRPGDumpDataSource class.
6 : * Author: Even Rouault, <even dot rouault at mines dash paris dot org>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010, Even Rouault
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_pgdump.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_string.h"
34 :
35 : CPL_CVSID("$Id: ogrpgdumpdatasource.cpp 22821 2011-07-28 17:54:47Z rouault $");
36 :
37 : /************************************************************************/
38 : /* OGRPGDumpDataSource() */
39 : /************************************************************************/
40 :
41 6 : OGRPGDumpDataSource::OGRPGDumpDataSource(const char* pszName,
42 6 : char** papszOptions)
43 :
44 : {
45 6 : nLayers = 0;
46 6 : papoLayers = NULL;
47 6 : this->pszName = CPLStrdup(pszName);
48 6 : bTriedOpen = FALSE;
49 6 : fp = NULL;
50 6 : bInTransaction = FALSE;
51 6 : poLayerInCopyMode = NULL;
52 :
53 6 : const char *pszCRLFFormat = CSLFetchNameValue( papszOptions, "LINEFORMAT");
54 :
55 : int bUseCRLF;
56 6 : if( pszCRLFFormat == NULL )
57 : {
58 : #ifdef WIN32
59 : bUseCRLF = TRUE;
60 : #else
61 2 : bUseCRLF = FALSE;
62 : #endif
63 : }
64 4 : else if( EQUAL(pszCRLFFormat,"CRLF") )
65 2 : bUseCRLF = TRUE;
66 2 : else if( EQUAL(pszCRLFFormat,"LF") )
67 2 : bUseCRLF = FALSE;
68 : else
69 : {
70 : CPLError( CE_Warning, CPLE_AppDefined,
71 : "LINEFORMAT=%s not understood, use one of CRLF or LF.",
72 0 : pszCRLFFormat );
73 : #ifdef WIN32
74 : bUseCRLF = TRUE;
75 : #else
76 0 : bUseCRLF = FALSE;
77 : #endif
78 : }
79 6 : pszEOL = (bUseCRLF) ? "\r\n" : "\n";
80 6 : }
81 :
82 : /************************************************************************/
83 : /* ~OGRPGDumpDataSource() */
84 : /************************************************************************/
85 :
86 6 : OGRPGDumpDataSource::~OGRPGDumpDataSource()
87 :
88 : {
89 : int i;
90 :
91 6 : if (fp)
92 : {
93 6 : Commit();
94 6 : VSIFCloseL(fp);
95 6 : fp = NULL;
96 : }
97 :
98 12 : for(i=0;i<nLayers;i++)
99 6 : delete papoLayers[i];
100 6 : CPLFree(papoLayers);
101 6 : CPLFree(pszName);
102 6 : }
103 :
104 : /************************************************************************/
105 : /* StartTransaction() */
106 : /************************************************************************/
107 :
108 6 : void OGRPGDumpDataSource::StartTransaction()
109 : {
110 6 : if (bInTransaction)
111 0 : return;
112 6 : bInTransaction = TRUE;
113 6 : Log("BEGIN");
114 : }
115 :
116 : /************************************************************************/
117 : /* Commit() */
118 : /************************************************************************/
119 :
120 12 : void OGRPGDumpDataSource::Commit()
121 : {
122 12 : EndCopy();
123 :
124 12 : if (!bInTransaction)
125 6 : return;
126 6 : bInTransaction = FALSE;
127 6 : Log("COMMIT");
128 : }
129 :
130 : /************************************************************************/
131 : /* LaunderName() */
132 : /************************************************************************/
133 :
134 32 : char *OGRPGDumpDataSource::LaunderName( const char *pszSrcName )
135 :
136 : {
137 32 : char *pszSafeName = CPLStrdup( pszSrcName );
138 :
139 236 : for( int i = 0; pszSafeName[i] != '\0'; i++ )
140 : {
141 204 : pszSafeName[i] = (char) tolower( pszSafeName[i] );
142 204 : if( pszSafeName[i] == '\'' || pszSafeName[i] == '-' || pszSafeName[i] == '#' )
143 0 : pszSafeName[i] = '_';
144 : }
145 :
146 32 : if( strcmp(pszSrcName,pszSafeName) != 0 )
147 : CPLDebug("PG","LaunderName('%s') -> '%s'",
148 26 : pszSrcName, pszSafeName);
149 :
150 32 : return pszSafeName;
151 : }
152 :
153 : /************************************************************************/
154 : /* CreateLayer() */
155 : /************************************************************************/
156 :
157 : OGRLayer *
158 6 : OGRPGDumpDataSource::CreateLayer( const char * pszLayerName,
159 : OGRSpatialReference *poSRS,
160 : OGRwkbGeometryType eType,
161 : char ** papszOptions )
162 :
163 : {
164 6 : CPLString osCommand;
165 6 : const char *pszGeomType = NULL;
166 6 : char *pszTableName = NULL;
167 6 : char *pszSchemaName = NULL;
168 6 : int nDimension = 3;
169 6 : int bHavePostGIS = TRUE;
170 :
171 6 : const char* pszFIDColumnName = CSLFetchNameValue(papszOptions, "FID");
172 6 : CPLString osFIDColumnName;
173 6 : if (pszFIDColumnName == NULL)
174 6 : osFIDColumnName = "OGC_FID";
175 : else
176 : {
177 0 : if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) )
178 : {
179 0 : char* pszLaunderedFid = LaunderName(pszFIDColumnName);
180 0 : osFIDColumnName += OGRPGDumpEscapeColumnName(pszLaunderedFid);
181 0 : CPLFree(pszLaunderedFid);
182 : }
183 : else
184 0 : osFIDColumnName += OGRPGDumpEscapeColumnName(pszFIDColumnName);
185 : }
186 6 : pszFIDColumnName = osFIDColumnName.c_str();
187 :
188 6 : if (strncmp(pszLayerName, "pg", 2) == 0)
189 : {
190 : CPLError(CE_Warning, CPLE_AppDefined,
191 0 : "The layer name should not begin by 'pg' as it is a reserved prefix");
192 : }
193 :
194 : //bHavePostGIS = CSLFetchBoolean(papszOptions,"POSTGIS", TRUE);
195 :
196 6 : int bCreateTable = CSLFetchBoolean(papszOptions,"CREATE_TABLE", TRUE);
197 6 : int bCreateSchema = CSLFetchBoolean(papszOptions,"CREATE_SCHEMA", TRUE);
198 6 : const char* pszDropTable = CSLFetchNameValueDef(papszOptions,"DROP_TABLE", "YES");
199 :
200 6 : if( wkbFlatten(eType) == eType )
201 6 : nDimension = 2;
202 :
203 6 : if( CSLFetchNameValue( papszOptions, "DIM") != NULL )
204 2 : nDimension = atoi(CSLFetchNameValue( papszOptions, "DIM"));
205 :
206 : /* Should we turn layers with None geometry type as Unknown/GEOMETRY */
207 : /* so they are still recorded in geometry_columns table ? (#4012) */
208 : int bNoneAsUnknown = CSLTestBoolean(CSLFetchNameValueDef(
209 6 : papszOptions, "NONE_AS_UNKNOWN", "NO"));
210 6 : if (bNoneAsUnknown && eType == wkbNone)
211 0 : eType = wkbUnknown;
212 6 : else if (eType == wkbNone)
213 2 : bHavePostGIS = FALSE;
214 :
215 : int bExtractSchemaFromLayerName = CSLTestBoolean(CSLFetchNameValueDef(
216 6 : papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES"));
217 :
218 : /* Postgres Schema handling:
219 : Extract schema name from input layer name or passed with -lco SCHEMA.
220 : Set layer name to "schema.table" or to "table" if schema == current_schema()
221 : Usage without schema name is backwards compatible
222 : */
223 6 : const char* pszDotPos = strstr(pszLayerName,".");
224 6 : if ( pszDotPos != NULL && bExtractSchemaFromLayerName )
225 : {
226 0 : int length = pszDotPos - pszLayerName;
227 0 : pszSchemaName = (char*)CPLMalloc(length+1);
228 0 : strncpy(pszSchemaName, pszLayerName, length);
229 0 : pszSchemaName[length] = '\0';
230 :
231 0 : if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) )
232 0 : pszTableName = LaunderName( pszDotPos + 1 ); //skip "."
233 : else
234 0 : pszTableName = CPLStrdup( pszDotPos + 1 ); //skip "."
235 : }
236 : else
237 : {
238 6 : pszSchemaName = NULL;
239 6 : if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) )
240 6 : pszTableName = LaunderName( pszLayerName ); //skip "."
241 : else
242 0 : pszTableName = CPLStrdup( pszLayerName ); //skip "."
243 : }
244 :
245 6 : Commit();
246 :
247 : /* -------------------------------------------------------------------- */
248 : /* Set the default schema for the layers. */
249 : /* -------------------------------------------------------------------- */
250 6 : if( CSLFetchNameValue( papszOptions, "SCHEMA" ) != NULL )
251 : {
252 4 : CPLFree(pszSchemaName);
253 4 : pszSchemaName = CPLStrdup(CSLFetchNameValue( papszOptions, "SCHEMA" ));
254 4 : if (bCreateSchema)
255 : {
256 4 : osCommand.Printf("CREATE SCHEMA \"%s\"", pszSchemaName);
257 4 : Log(osCommand);
258 : }
259 : }
260 :
261 6 : if ( pszSchemaName == NULL)
262 : {
263 2 : pszSchemaName = CPLStrdup("public");
264 : }
265 :
266 : /* -------------------------------------------------------------------- */
267 : /* Do we already have this layer? */
268 : /* -------------------------------------------------------------------- */
269 : int iLayer;
270 :
271 6 : for( iLayer = 0; iLayer < nLayers; iLayer++ )
272 : {
273 0 : if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) )
274 : {
275 : CPLError( CE_Failure, CPLE_AppDefined,
276 : "Layer %s already exists, CreateLayer failed.\n",
277 0 : pszLayerName );
278 0 : CPLFree( pszTableName );
279 0 : CPLFree( pszSchemaName );
280 0 : return NULL;
281 : }
282 : }
283 :
284 :
285 6 : if (bCreateTable && (EQUAL(pszDropTable, "YES") ||
286 : EQUAL(pszDropTable, "ON") ||
287 : EQUAL(pszDropTable, "TRUE") ||
288 : EQUAL(pszDropTable, "IF_EXISTS")))
289 : {
290 6 : if (EQUAL(pszDropTable, "IF_EXISTS"))
291 0 : osCommand.Printf("DROP TABLE IF EXISTS \"%s\".\"%s\" CASCADE", pszSchemaName, pszTableName );
292 : else
293 6 : osCommand.Printf("DROP TABLE \"%s\".\"%s\" CASCADE", pszSchemaName, pszTableName );
294 6 : Log(osCommand);
295 : }
296 :
297 : /* -------------------------------------------------------------------- */
298 : /* Handle the GEOM_TYPE option. */
299 : /* -------------------------------------------------------------------- */
300 6 : pszGeomType = CSLFetchNameValue( papszOptions, "GEOM_TYPE" );
301 6 : if( pszGeomType == NULL )
302 : {
303 6 : pszGeomType = "geometry";
304 : }
305 :
306 6 : if( !EQUAL(pszGeomType,"geometry") && !EQUAL(pszGeomType, "geography"))
307 : {
308 : CPLError( CE_Failure, CPLE_AppDefined,
309 : "GEOM_TYPE in PostGIS enabled databases must be 'geometry' or 'geography'.\n"
310 : "Creation of layer %s with GEOM_TYPE %s has failed.",
311 0 : pszLayerName, pszGeomType );
312 0 : CPLFree( pszTableName );
313 0 : CPLFree( pszSchemaName );
314 0 : return NULL;
315 : }
316 :
317 : /* -------------------------------------------------------------------- */
318 : /* Try to get the SRS Id of this spatial reference system, */
319 : /* adding tot the srs table if needed. */
320 : /* -------------------------------------------------------------------- */
321 6 : int nSRSId = -1;
322 :
323 6 : if( CSLFetchNameValue( papszOptions, "SRID") != NULL )
324 2 : nSRSId = atoi(CSLFetchNameValue( papszOptions, "SRID"));
325 : else
326 : {
327 4 : if (poSRS)
328 : {
329 0 : const char* pszAuthorityName = poSRS->GetAuthorityName(NULL);
330 0 : if( pszAuthorityName != NULL && EQUAL( pszAuthorityName, "EPSG" ) )
331 : {
332 : /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */
333 0 : nSRSId = atoi( poSRS->GetAuthorityCode(NULL) );
334 : }
335 : else
336 : {
337 0 : const char* pszGeogCSName = poSRS->GetAttrValue("GEOGCS");
338 0 : if (pszGeogCSName != NULL && EQUAL(pszGeogCSName, "GCS_WGS_1984"))
339 0 : nSRSId = 4326;
340 : }
341 : }
342 : }
343 :
344 6 : CPLString osEscapedTableNameSingleQuote = OGRPGDumpEscapeString(pszTableName, -1, "");
345 6 : const char* pszEscapedTableNameSingleQuote = osEscapedTableNameSingleQuote.c_str();
346 :
347 6 : const char *pszGeometryType = OGRToOGCGeomType(eType);
348 :
349 6 : const char *pszGFldName = NULL;
350 6 : if( bHavePostGIS && !EQUAL(pszGeomType, "geography"))
351 : {
352 4 : if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL )
353 2 : pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
354 : else
355 2 : pszGFldName = "wkb_geometry";
356 :
357 : /* Sometimes there is an old cruft entry in the geometry_columns
358 : * table if things were not properly cleaned up before. We make
359 : * an effort to clean out such cruft.
360 : */
361 : osCommand.Printf(
362 : "DELETE FROM geometry_columns WHERE f_table_name = %s AND f_table_schema = '%s'",
363 4 : pszEscapedTableNameSingleQuote, pszSchemaName );
364 4 : if (bCreateTable)
365 4 : Log(osCommand);
366 : }
367 :
368 :
369 6 : StartTransaction();
370 :
371 : /* -------------------------------------------------------------------- */
372 : /* Create a basic table with the FID. Also include the */
373 : /* geometry if this is not a PostGIS enabled table. */
374 : /* -------------------------------------------------------------------- */
375 :
376 6 : CPLString osCreateTable;
377 : int bTemporary = CSLFetchNameValue( papszOptions, "TEMPORARY" ) != NULL &&
378 6 : CSLTestBoolean(CSLFetchNameValue( papszOptions, "TEMPORARY" ));
379 6 : if (bTemporary)
380 : {
381 0 : CPLFree(pszSchemaName);
382 0 : pszSchemaName = CPLStrdup("pg_temp_1");
383 0 : osCreateTable.Printf("CREATE TEMPORARY TABLE \"%s\"", pszTableName);
384 : }
385 : else
386 6 : osCreateTable.Printf("CREATE TABLE \"%s\".\"%s\"", pszSchemaName, pszTableName);
387 :
388 6 : if( !bHavePostGIS )
389 : {
390 2 : if (eType == wkbNone)
391 : osCommand.Printf(
392 : "%s ( "
393 : " %s SERIAL, "
394 : " CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
395 2 : osCreateTable.c_str(), pszFIDColumnName, pszTableName, pszFIDColumnName );
396 : else
397 : osCommand.Printf(
398 : "%s ( "
399 : " %s SERIAL, "
400 : " WKB_GEOMETRY %s, "
401 : " CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
402 0 : osCreateTable.c_str(), pszFIDColumnName, pszGeomType, pszTableName, pszFIDColumnName );
403 : }
404 4 : else if ( EQUAL(pszGeomType, "geography") )
405 : {
406 0 : if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL )
407 0 : pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
408 : else
409 0 : pszGFldName = "the_geog";
410 :
411 0 : if (nSRSId)
412 : osCommand.Printf(
413 : "%s ( %s SERIAL, \"%s\" geography(%s%s,%d), CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
414 0 : osCreateTable.c_str(), pszFIDColumnName, pszGFldName, pszGeometryType, nDimension == 2 ? "" : "Z", nSRSId, pszTableName, pszFIDColumnName );
415 : else
416 : osCommand.Printf(
417 : "%s ( %s SERIAL, \"%s\" geography(%s%s), CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
418 0 : osCreateTable.c_str(), pszFIDColumnName, pszGFldName, pszGeometryType, nDimension == 2 ? "" : "Z", pszTableName, pszFIDColumnName );
419 : }
420 : else
421 : {
422 : osCommand.Printf(
423 : "%s ( %s SERIAL, CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )",
424 4 : osCreateTable.c_str(), pszFIDColumnName, pszTableName, pszFIDColumnName );
425 : }
426 :
427 6 : if (bCreateTable)
428 6 : Log(osCommand);
429 :
430 : /* -------------------------------------------------------------------- */
431 : /* Eventually we should be adding this table to a table of */
432 : /* "geometric layers", capturing the WKT projection, and */
433 : /* perhaps some other housekeeping. */
434 : /* -------------------------------------------------------------------- */
435 6 : if( bCreateTable && bHavePostGIS && !EQUAL(pszGeomType, "geography"))
436 : {
437 : osCommand.Printf(
438 : "SELECT AddGeometryColumn('%s',%s,'%s',%d,'%s',%d)",
439 : pszSchemaName, pszEscapedTableNameSingleQuote, pszGFldName,
440 4 : nSRSId, pszGeometryType, nDimension );
441 4 : Log(osCommand);
442 : }
443 :
444 6 : if( bCreateTable && bHavePostGIS )
445 : {
446 : /* -------------------------------------------------------------------- */
447 : /* Create the spatial index. */
448 : /* */
449 : /* We're doing this before we add geometry and record to the table */
450 : /* so this may not be exactly the best way to do it. */
451 : /* -------------------------------------------------------------------- */
452 4 : const char *pszSI = CSLFetchNameValue( papszOptions, "SPATIAL_INDEX" );
453 4 : if( pszSI == NULL || CSLTestBoolean(pszSI) )
454 : {
455 : osCommand.Printf("CREATE INDEX \"%s_geom_idx\" "
456 : "ON \"%s\".\"%s\" "
457 : "USING GIST (\"%s\")",
458 4 : pszTableName, pszSchemaName, pszTableName, pszGFldName);
459 :
460 4 : Log(osCommand);
461 : }
462 : }
463 :
464 : /* -------------------------------------------------------------------- */
465 : /* Create the layer object. */
466 : /* -------------------------------------------------------------------- */
467 : OGRPGDumpLayer *poLayer;
468 :
469 6 : int bWriteAsHex = !CSLFetchBoolean(papszOptions,"WRITE_EWKT_GEOM",FALSE);
470 :
471 : poLayer = new OGRPGDumpLayer( this, pszSchemaName, pszTableName, pszGFldName,
472 6 : pszFIDColumnName, nDimension, nSRSId, bWriteAsHex, bCreateTable );
473 12 : poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) );
474 6 : poLayer->SetPrecisionFlag( CSLFetchBoolean(papszOptions,"PRECISION",TRUE));
475 :
476 : /* -------------------------------------------------------------------- */
477 : /* Add layer to data source layer list. */
478 : /* -------------------------------------------------------------------- */
479 : papoLayers = (OGRPGDumpLayer **)
480 6 : CPLRealloc( papoLayers, sizeof(OGRPGDumpLayer *) * (nLayers+1) );
481 :
482 6 : papoLayers[nLayers++] = poLayer;
483 :
484 6 : CPLFree( pszTableName );
485 6 : CPLFree( pszSchemaName );
486 :
487 6 : return poLayer;
488 : }
489 :
490 : /************************************************************************/
491 : /* TestCapability() */
492 : /************************************************************************/
493 :
494 0 : int OGRPGDumpDataSource::TestCapability( const char * pszCap )
495 :
496 : {
497 0 : if( EQUAL(pszCap,ODsCCreateLayer) )
498 0 : return TRUE;
499 : else
500 0 : return FALSE;
501 : }
502 :
503 : /************************************************************************/
504 : /* GetLayer() */
505 : /************************************************************************/
506 :
507 0 : OGRLayer *OGRPGDumpDataSource::GetLayer( int iLayer )
508 :
509 : {
510 0 : if( iLayer < 0 || iLayer >= nLayers )
511 0 : return NULL;
512 : else
513 0 : return papoLayers[iLayer];
514 : }
515 :
516 : /************************************************************************/
517 : /* Log() */
518 : /************************************************************************/
519 :
520 138 : void OGRPGDumpDataSource::Log(const char* pszStr, int bAddSemiColumn)
521 : {
522 138 : if (fp == NULL)
523 : {
524 6 : if (bTriedOpen)
525 0 : return;
526 6 : bTriedOpen = TRUE;
527 6 : fp = VSIFOpenL(pszName, "wb");
528 6 : if (fp == NULL)
529 : {
530 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszName);
531 0 : return;
532 : }
533 : }
534 :
535 138 : if (bAddSemiColumn)
536 94 : VSIFPrintfL(fp, "%s;%s", pszStr, pszEOL);
537 : else
538 44 : VSIFPrintfL(fp, "%s%s", pszStr, pszEOL);
539 : }
540 :
541 : /************************************************************************/
542 : /* StartCopy() */
543 : /************************************************************************/
544 4 : void OGRPGDumpDataSource::StartCopy( OGRPGDumpLayer *poPGLayer )
545 : {
546 4 : EndCopy();
547 4 : poLayerInCopyMode = poPGLayer;
548 4 : }
549 :
550 : /************************************************************************/
551 : /* EndCopy() */
552 : /************************************************************************/
553 16 : OGRErr OGRPGDumpDataSource::EndCopy( )
554 : {
555 16 : if( poLayerInCopyMode != NULL )
556 : {
557 4 : OGRErr result = poLayerInCopyMode->EndCopy();
558 4 : poLayerInCopyMode = NULL;
559 :
560 4 : return result;
561 : }
562 : else
563 12 : return OGRERR_NONE;
564 : }
|