1 /******************************************************************************
2 * $Id: FGdbDriver.cpp 29180 2015-05-10 15:07:48Z rouault $
3 *
4 * Project: OpenGIS Simple Features Reference Implementation
5 * Purpose: Implements FileGDB OGR driver.
6 * Author: Ragi Yaser Burhum, ragi@burhum.com
7 * Paul Ramsey, pramsey at cleverelephant.ca
8 *
9 ******************************************************************************
10 * Copyright (c) 2010, Ragi Yaser Burhum
11 * Copyright (c) 2011, Paul Ramsey <pramsey at cleverelephant.ca>
12 * Copyright (c) 2011-2013, Even Rouault <even dot rouault at mines-paris dot org>
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a
15 * copy of this software and associated documentation files (the "Software"),
16 * to deal in the Software without restriction, including without limitation
17 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 * and/or sell copies of the Software, and to permit persons to whom the
19 * Software is furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included
22 * in all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 * DEALINGS IN THE SOFTWARE.
31 ****************************************************************************/
32
33 #include "ogr_fgdb.h"
34 #include "cpl_conv.h"
35 #include "FGdbUtils.h"
36 #include "cpl_multiproc.h"
37 #include "ogrmutexeddatasource.h"
38
39 CPL_CVSID("$Id: FGdbDriver.cpp 29180 2015-05-10 15:07:48Z rouault $");
40
41 extern "C" void RegisterOGRFileGDB();
42
43 /************************************************************************/
44 /* FGdbDriver() */
45 /************************************************************************/
FGdbDriver()46 FGdbDriver::FGdbDriver(): OGRSFDriver(), hMutex(NULL)
47 {
48 }
49
50 /************************************************************************/
51 /* ~FGdbDriver() */
52 /************************************************************************/
~FGdbDriver()53 FGdbDriver::~FGdbDriver()
54
55 {
56 if( oMapConnections.size() != 0 )
57 CPLDebug("FileGDB", "Remaining %d connections. Bug?",
58 (int)oMapConnections.size());
59 if( hMutex != NULL )
60 CPLDestroyMutex(hMutex);
61 hMutex = NULL;
62 }
63
64
65 /************************************************************************/
66 /* GetName() */
67 /************************************************************************/
68
GetName()69 const char *FGdbDriver::GetName()
70
71 {
72 return "FileGDB";
73 }
74
75 /************************************************************************/
76 /* Open() */
77 /************************************************************************/
78
Open(const char * pszFilename,int bUpdate)79 OGRDataSource *FGdbDriver::Open( const char* pszFilename, int bUpdate )
80
81 {
82 // First check if we have to do any work.
83 int nLen = strlen(pszFilename);
84 if(! ((nLen >= 4 && EQUAL(pszFilename + nLen - 4, ".gdb")) ||
85 (nLen >= 5 && EQUAL(pszFilename + nLen - 5, ".gdb/"))) )
86 return NULL;
87
88 long hr;
89
90 /* Check that the filename is really a directory, to avoid confusion with */
91 /* Garmin MapSource - gdb format which can be a problem when the FileGDB */
92 /* driver is loaded as a plugin, and loaded before the GPSBabel driver */
93 /* (http://trac.osgeo.org/osgeo4w/ticket/245) */
94 VSIStatBuf stat;
95 if( CPLStat( pszFilename, &stat ) != 0 || !VSI_ISDIR(stat.st_mode) )
96 {
97 return NULL;
98 }
99
100 CPLMutexHolderD(&hMutex);
101 Geodatabase* pGeoDatabase = NULL;
102
103 FGdbDatabaseConnection* pConnection = oMapConnections[pszFilename];
104 if( pConnection != NULL )
105 {
106 pGeoDatabase = pConnection->m_pGeodatabase;
107 pConnection->m_nRefCount ++;
108 CPLDebug("FileGDB", "ref_count of %s = %d now", pszFilename,
109 pConnection->m_nRefCount);
110 }
111 else
112 {
113 pGeoDatabase = new Geodatabase;
114 hr = ::OpenGeodatabase(StringToWString(pszFilename), *pGeoDatabase);
115
116 if (FAILED(hr) || pGeoDatabase == NULL)
117 {
118 delete pGeoDatabase;
119
120 if( OGRGetDriverByName("OpenFileGDB") != NULL && bUpdate == FALSE )
121 {
122 std::wstring fgdb_error_desc_w;
123 std::string fgdb_error_desc("Unknown error");
124 fgdbError er;
125 er = FileGDBAPI::ErrorInfo::GetErrorDescription(hr, fgdb_error_desc_w);
126 if ( er == S_OK )
127 {
128 fgdb_error_desc = WStringToString(fgdb_error_desc_w);
129 }
130 CPLDebug("FileGDB", "Cannot open %s with FileGDB driver: %s. Failing silently so OpenFileGDB can be tried",
131 pszFilename,
132 fgdb_error_desc.c_str());
133 }
134 else
135 {
136 GDBErr(hr, "Failed to open Geodatabase");
137 }
138 oMapConnections.erase(pszFilename);
139 return NULL;
140 }
141
142 CPLDebug("FileGDB", "Really opening %s", pszFilename);
143 pConnection = new FGdbDatabaseConnection(pGeoDatabase);
144 oMapConnections[pszFilename] = pConnection;
145 }
146
147 FGdbDataSource* pDS;
148
149 pDS = new FGdbDataSource(this, pConnection);
150
151 if(!pDS->Open( pszFilename, bUpdate ) )
152 {
153 delete pDS;
154 return NULL;
155 }
156 else
157 {
158 OGRMutexedDataSource* poMutexedDS =
159 new OGRMutexedDataSource(pDS, TRUE, hMutex, TRUE);
160 if( bUpdate )
161 return OGRCreateEmulatedTransactionDataSourceWrapper(poMutexedDS, this, TRUE, FALSE);
162 else
163 return poMutexedDS;
164 }
165 }
166
167 /***********************************************************************/
168 /* CreateDataSource() */
169 /***********************************************************************/
170
CreateDataSource(const char * conn,char ** papszOptions)171 OGRDataSource* FGdbDriver::CreateDataSource( const char * conn,
172 char **papszOptions)
173 {
174 long hr;
175 Geodatabase *pGeodatabase;
176 std::wstring wconn = StringToWString(conn);
177 int bUpdate = TRUE; // If we're creating, we must be writing.
178 VSIStatBuf stat;
179
180 CPLMutexHolderD(&hMutex);
181
182 /* We don't support options yet, so warn if they send us some */
183 if ( papszOptions )
184 {
185 /* TODO: warning, ignoring options */
186 }
187
188 /* Only accept names of form "filename.gdb" and */
189 /* also .gdb.zip to be able to return FGDB with MapServer OGR output (#4199) */
190 const char* pszExt = CPLGetExtension(conn);
191 if ( !(EQUAL(pszExt,"gdb") || EQUAL(pszExt, "zip")) )
192 {
193 CPLError( CE_Failure, CPLE_AppDefined,
194 "FGDB data source name must use 'gdb' extension.\n" );
195 return NULL;
196 }
197
198 /* Don't try to create on top of something already there */
199 if( CPLStat( conn, &stat ) == 0 )
200 {
201 CPLError( CE_Failure, CPLE_AppDefined,
202 "%s already exists.\n", conn );
203 return NULL;
204 }
205
206 /* Try to create the geodatabase */
207 pGeodatabase = new Geodatabase; // Create on heap so we can store it in the Datasource
208 hr = CreateGeodatabase(wconn, *pGeodatabase);
209
210 /* Handle creation errors */
211 if ( S_OK != hr )
212 {
213 const char *errstr = "Error creating geodatabase (%s).\n";
214 if ( hr == -2147220653 )
215 errstr = "File already exists (%s).\n";
216 delete pGeodatabase;
217 CPLError( CE_Failure, CPLE_AppDefined, errstr, conn );
218 return NULL;
219 }
220
221 FGdbDatabaseConnection* pConnection = new FGdbDatabaseConnection(pGeodatabase);
222 oMapConnections[conn] = pConnection;
223
224 /* Ready to embed the Geodatabase in an OGR Datasource */
225 FGdbDataSource* pDS = new FGdbDataSource(this, pConnection);
226 if ( ! pDS->Open(conn, bUpdate) )
227 {
228 delete pDS;
229 return NULL;
230 }
231 else
232 return OGRCreateEmulatedTransactionDataSourceWrapper(
233 new OGRMutexedDataSource(pDS, TRUE, hMutex, TRUE), this,
234 TRUE, FALSE);
235 }
236
237 /************************************************************************/
238 /* StartTransaction() */
239 /************************************************************************/
240
StartTransaction(OGRDataSource * & poDSInOut,int & bOutHasReopenedDS)241 OGRErr FGdbDriver::StartTransaction(OGRDataSource*& poDSInOut, int& bOutHasReopenedDS)
242 {
243 CPLMutexHolderOptionalLockD(hMutex);
244
245 bOutHasReopenedDS = FALSE;
246
247 OGRMutexedDataSource* poMutexedDS = (OGRMutexedDataSource*)poDSInOut;
248 FGdbDataSource* poDS = (FGdbDataSource* )poMutexedDS->GetBaseDataSource();
249 if( !poDS->GetUpdate() )
250 return OGRERR_FAILURE;
251 FGdbDatabaseConnection* pConnection = poDS->GetConnection();
252 if( pConnection->GetRefCount() != 1 )
253 {
254 CPLError(CE_Failure, CPLE_AppDefined,
255 "Cannot start transaction as database is opened in another connection");
256 return OGRERR_FAILURE;
257 }
258 if( pConnection->IsLocked() )
259 {
260 CPLError(CE_Failure, CPLE_AppDefined,
261 "Transaction is already in progress");
262 return OGRERR_FAILURE;
263 }
264
265 bOutHasReopenedDS = TRUE;
266
267 CPLString osName(poMutexedDS->GetName());
268 if( osName[osName.size()-1] == '/' || osName[osName.size()-1] == '\\' )
269 osName.resize(osName.size()-1);
270
271 pConnection->m_nRefCount ++;
272 delete poDSInOut;
273 poDSInOut = NULL;
274 poMutexedDS = NULL;
275 poDS = NULL;
276
277 ::CloseGeodatabase(*(pConnection->m_pGeodatabase));
278 delete pConnection->m_pGeodatabase;
279 pConnection->m_pGeodatabase = NULL;
280
281 CPLString osEditedName(osName);
282 osEditedName += ".ogredited";
283
284 CPLPushErrorHandler(CPLQuietErrorHandler);
285 CPLUnlinkTree(osEditedName);
286 CPLPopErrorHandler();
287
288 OGRErr eErr = OGRERR_NONE;
289 CPLString osDatabaseToReopen;
290 if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") ||
291 CPLCopyTree( osEditedName, osName ) != 0 )
292 {
293 CPLError(CE_Failure, CPLE_AppDefined,
294 "Cannot backup geodatabase");
295 eErr = OGRERR_FAILURE;
296 osDatabaseToReopen = osName;
297 }
298 else
299 osDatabaseToReopen = osEditedName;
300
301 pConnection->m_pGeodatabase = new Geodatabase;
302 long hr = ::OpenGeodatabase(StringToWString(osDatabaseToReopen), *(pConnection->m_pGeodatabase));
303 if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE2") || FAILED(hr))
304 {
305 delete pConnection->m_pGeodatabase;
306 pConnection->m_pGeodatabase = NULL;
307 Release(osName);
308 GDBErr(hr, CPLSPrintf("Failed to open %s. Dataset should be closed",
309 osDatabaseToReopen.c_str()));
310
311 return OGRERR_FAILURE;
312 }
313
314 FGdbDataSource* pDS = new FGdbDataSource(this, pConnection);
315 pDS->Open(osName, TRUE);
316 poDSInOut = new OGRMutexedDataSource(pDS, TRUE, hMutex, TRUE);
317
318 if( eErr == OGRERR_NONE )
319 pConnection->SetLocked(TRUE);
320 return eErr;
321 }
322
323 /************************************************************************/
324 /* CommitTransaction() */
325 /************************************************************************/
326
CommitTransaction(OGRDataSource * & poDSInOut,int & bOutHasReopenedDS)327 OGRErr FGdbDriver::CommitTransaction(OGRDataSource*& poDSInOut, int& bOutHasReopenedDS)
328 {
329 CPLMutexHolderOptionalLockD(hMutex);
330
331 bOutHasReopenedDS = FALSE;
332
333
334 OGRMutexedDataSource* poMutexedDS = (OGRMutexedDataSource*)poDSInOut;
335 FGdbDataSource* poDS = (FGdbDataSource* )poMutexedDS->GetBaseDataSource();
336 FGdbDatabaseConnection* pConnection = poDS->GetConnection();
337 if( !pConnection->IsLocked() )
338 {
339 CPLError(CE_Failure, CPLE_NotSupported,
340 "No transaction in progress");
341 return OGRERR_FAILURE;
342 }
343
344 bOutHasReopenedDS = TRUE;
345
346 CPLString osName(poMutexedDS->GetName());
347 if( osName[osName.size()-1] == '/' || osName[osName.size()-1] == '\\' )
348 osName.resize(osName.size()-1);
349
350 pConnection->m_nRefCount ++;
351 delete poDSInOut;
352 poDSInOut = NULL;
353 poMutexedDS = NULL;
354 poDS = NULL;
355
356 ::CloseGeodatabase(*(pConnection->m_pGeodatabase));
357 delete pConnection->m_pGeodatabase;
358 pConnection->m_pGeodatabase = NULL;
359
360 CPLString osEditedName(osName);
361 osEditedName += ".ogredited";
362 CPLString osTmpName(osName);
363 osTmpName += ".ogrtmp";
364
365 /* Install the backup copy as the main database in 3 steps : */
366 /* first rename the main directory in .tmp */
367 /* then rename the edited copy under regular name */
368 /* and finally dispose the .tmp directory */
369 /* That way there's no risk definitely losing data */
370 if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") ||
371 VSIRename(osName, osTmpName) != 0 )
372 {
373 CPLError(CE_Failure, CPLE_AppDefined,
374 "Cannot rename %s to %s. Edited database during transaction is in %s"
375 "Dataset should be closed",
376 osName.c_str(), osTmpName.c_str(), osEditedName.c_str());
377 pConnection->SetLocked(FALSE);
378 Release(osName);
379 return OGRERR_FAILURE;
380 }
381
382 if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE2") ||
383 VSIRename(osEditedName, osName) != 0 )
384 {
385 CPLError(CE_Failure, CPLE_AppDefined,
386 "Cannot rename %s to %s. The original geodatabase is in '%s'. "
387 "Dataset should be closed",
388 osEditedName.c_str(), osName.c_str(), osTmpName.c_str());
389 pConnection->SetLocked(FALSE);
390 Release(osName);
391 return OGRERR_FAILURE;
392 }
393
394 if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE3") ||
395 CPLUnlinkTree(osTmpName) != 0 )
396 {
397 CPLError(CE_Warning, CPLE_AppDefined,
398 "Cannot remove %s. Manual cleanup required", osTmpName.c_str());
399 }
400
401 pConnection->m_pGeodatabase = new Geodatabase;
402 long hr = ::OpenGeodatabase(StringToWString(osName), *(pConnection->m_pGeodatabase));
403 if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE4") || FAILED(hr))
404 {
405 delete pConnection->m_pGeodatabase;
406 pConnection->m_pGeodatabase = NULL;
407 pConnection->SetLocked(FALSE);
408 Release(osName);
409 GDBErr(hr, "Failed to re-open Geodatabase. Dataset should be closed");
410 return OGRERR_FAILURE;
411 }
412
413 FGdbDataSource* pDS = new FGdbDataSource(this, pConnection);
414 pDS->Open(osName, TRUE);
415 poDSInOut = new OGRMutexedDataSource(pDS, TRUE, hMutex, TRUE);
416
417 pConnection->SetLocked(FALSE);
418
419 return OGRERR_NONE;
420 }
421
422 /************************************************************************/
423 /* RollbackTransaction() */
424 /************************************************************************/
425
RollbackTransaction(OGRDataSource * & poDSInOut,int & bOutHasReopenedDS)426 OGRErr FGdbDriver::RollbackTransaction(OGRDataSource*& poDSInOut, int& bOutHasReopenedDS)
427 {
428 CPLMutexHolderOptionalLockD(hMutex);
429
430 bOutHasReopenedDS = FALSE;
431
432 OGRMutexedDataSource* poMutexedDS = (OGRMutexedDataSource*)poDSInOut;
433 FGdbDataSource* poDS = (FGdbDataSource* )poMutexedDS->GetBaseDataSource();
434 FGdbDatabaseConnection* pConnection = poDS->GetConnection();
435 if( !pConnection->IsLocked() )
436 {
437 CPLError(CE_Failure, CPLE_NotSupported,
438 "No transaction in progress");
439 return OGRERR_FAILURE;
440 }
441
442 bOutHasReopenedDS = TRUE;
443
444 CPLString osName(poMutexedDS->GetName());
445 if( osName[osName.size()-1] == '/' || osName[osName.size()-1] == '\\' )
446 osName.resize(osName.size()-1);
447
448 pConnection->m_nRefCount ++;
449 delete poDSInOut;
450 poDSInOut = NULL;
451 poMutexedDS = NULL;
452 poDS = NULL;
453
454 ::CloseGeodatabase(*(pConnection->m_pGeodatabase));
455 delete pConnection->m_pGeodatabase;
456 pConnection->m_pGeodatabase = NULL;
457
458 CPLString osEditedName(osName);
459 osEditedName += ".ogredited";
460
461 OGRErr eErr = OGRERR_NONE;
462 if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") ||
463 CPLUnlinkTree(osEditedName) != 0 )
464 {
465 CPLError(CE_Warning, CPLE_AppDefined,
466 "Cannot remove %s. Manual cleanup required", osEditedName.c_str());
467 eErr = OGRERR_FAILURE;
468 }
469
470 pConnection->m_pGeodatabase = new Geodatabase;
471 long hr = ::OpenGeodatabase(StringToWString(osName), *(pConnection->m_pGeodatabase));
472 if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE2") ||
473 FAILED(hr))
474 {
475 delete pConnection->m_pGeodatabase;
476 pConnection->m_pGeodatabase = NULL;
477 pConnection->SetLocked(FALSE);
478 Release(osName);
479 GDBErr(hr, "Failed to re-open Geodatabase. Dataset should be closed");
480 return OGRERR_FAILURE;
481 }
482
483 FGdbDataSource* pDS = new FGdbDataSource(this, pConnection);
484 pDS->Open(osName, TRUE);
485 poDSInOut = new OGRMutexedDataSource(pDS, TRUE, hMutex, TRUE);
486
487 pConnection->SetLocked(FALSE);
488
489 return eErr;
490 }
491
492 /***********************************************************************/
493 /* Release() */
494 /***********************************************************************/
495
Release(const char * pszName)496 void FGdbDriver::Release(const char* pszName)
497 {
498 CPLMutexHolderOptionalLockD(hMutex);
499
500 FGdbDatabaseConnection* pConnection = oMapConnections[pszName];
501 if( pConnection != NULL )
502 {
503 pConnection->m_nRefCount --;
504 CPLDebug("FileGDB", "ref_count of %s = %d now", pszName,
505 pConnection->m_nRefCount);
506 if( pConnection->m_nRefCount == 0 )
507 {
508 if( pConnection->m_pGeodatabase != NULL )
509 {
510 CPLDebug("FileGDB", "Really closing %s now", pszName);
511 ::CloseGeodatabase(*(pConnection->m_pGeodatabase));
512 delete pConnection->m_pGeodatabase;
513 pConnection->m_pGeodatabase = NULL;
514 }
515 delete pConnection;
516 oMapConnections.erase(pszName);
517 }
518 }
519 }
520
521 /***********************************************************************/
522 /* TestCapability() */
523 /***********************************************************************/
524
TestCapability(const char * pszCap)525 int FGdbDriver::TestCapability( const char * pszCap )
526 {
527 if (EQUAL(pszCap, ODrCCreateDataSource) )
528 return TRUE;
529
530 else if (EQUAL(pszCap, ODrCDeleteDataSource) )
531 return TRUE;
532
533 return FALSE;
534 }
535 /************************************************************************/
536 /* DeleteDataSource() */
537 /************************************************************************/
538
DeleteDataSource(const char * pszDataSource)539 OGRErr FGdbDriver::DeleteDataSource( const char *pszDataSource )
540 {
541 CPLMutexHolderD(&hMutex);
542
543 std::wstring wstr = StringToWString(pszDataSource);
544
545 long hr;
546
547 if (S_OK != (hr = ::DeleteGeodatabase(wstr)))
548 {
549 GDBErr(hr, "Failed to delete Geodatabase");
550 return OGRERR_FAILURE;
551 }
552
553 return OGRERR_NONE;
554 }
555
556 /***********************************************************************/
557 /* RegisterOGRFileGDB() */
558 /***********************************************************************/
559
RegisterOGRFileGDB()560 void RegisterOGRFileGDB()
561
562 {
563 if (! GDAL_CHECK_VERSION("OGR FGDB"))
564 return;
565 OGRSFDriver* poDriver = new FGdbDriver;
566 poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
567 "ESRI FileGDB" );
568 poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gdb" );
569 poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
570 "drv_filegdb.html" );
571
572 poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, "<CreationOptionList/>" );
573
574 poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST,
575 "<LayerCreationOptionList>"
576 " <Option name='FEATURE_DATASET' type='string' description='FeatureDataset folder into to put the new layer'/>"
577 " <Option name='GEOMETRY_NAME' type='string' description='Name of geometry column' default='SHAPE'/>"
578 " <Option name='GEOMETRY_NULLABLE' type='boolean' description='Whether the values of the geometry column can be NULL' default='YES'/>"
579 " <Option name='FID' type='string' description='Name of OID column' default='OBJECTID' deprecated_alias='OID_NAME'/>"
580 " <Option name='XYTOLERANCE' type='float' description='Snapping tolerance, used for advanced ArcGIS features like network and topology rules, on 2D coordinates, in the units of the CRS'/>"
581 " <Option name='ZTOLERANCE' type='float' description='Snapping tolerance, used for advanced ArcGIS features like network and topology rules, on Z coordinates, in the units of the CRS'/>"
582 " <Option name='XORIGIN' type='float' description='X origin of the coordinate precision grid'/>"
583 " <Option name='YORIGIN' type='float' description='Y origin of the coordinate precision grid'/>"
584 " <Option name='ZORIGIN' type='float' description='Z origin of the coordinate precision grid'/>"
585 " <Option name='XYSCALE' type='float' description='X,Y scale of the coordinate precision grid'/>"
586 " <Option name='ZSCALE' type='float' description='Z scale of the coordinate precision grid'/>"
587 " <Option name='XML_DEFINITION' type='string' description='XML definition to create the new table. The root node of such a XML definition must be a <esri:DataElement> element conformant to FileGDBAPI.xsd'/>"
588 " <Option name='CREATE_MULTIPATCH' type='boolean' description='Whether to write geometries of layers of type MultiPolygon as MultiPatch' default='NO'/>"
589 " <Option name='COLUMN_TYPES' type='string' description='A list of strings of format field_name=fgdb_filed_type (separated by comma) to force the FileGDB column type of fields to be created'/>"
590 " <Option name='CONFIGURATION_KEYWORD' type='string-select' description='Customize how data is stored. By default text in UTF-8 and data up to 1TB'>"
591 " <Value>DEFAULTS</Value>"
592 " <Value>TEXT_UTF16</Value>"
593 " <Value>MAX_FILE_SIZE_4GB</Value>"
594 " <Value>MAX_FILE_SIZE_256TB</Value>"
595 " <Value>GEOMETRY_OUTOFLINE</Value>"
596 " <Value>BLOB_OUTOFLINE</Value>"
597 " <Value>GEOMETRY_AND_BLOB_OUTOFLINE</Value>"
598 " </Option>"
599 "</LayerCreationOptionList>");
600
601 poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Real String Date DateTime Binary" );
602 poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" );
603 poDriver->SetMetadataItem( GDAL_DCAP_DEFAULT_FIELDS, "YES" );
604 poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES" );
605
606 OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver);
607 }
608
609