Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements management of FileGDB field write support
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2022, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 :
15 : #include "filegdbtable.h"
16 : #include "filegdbtable_priv.h"
17 :
18 : #include <algorithm>
19 : #include <limits>
20 :
21 : #include "cpl_string.h"
22 : #include "cpl_time.h"
23 :
24 : #include "ogr_core.h"
25 : #include "ogr_api.h"
26 :
27 : namespace OpenFileGDB
28 : {
29 :
30 : /************************************************************************/
31 : /* CreateField() */
32 : /************************************************************************/
33 :
34 13101 : bool FileGDBTable::CreateField(std::unique_ptr<FileGDBField> &&psField)
35 : {
36 13101 : if (!m_bUpdate)
37 0 : return false;
38 :
39 : // Encoded on a uint16_t
40 13101 : if (m_apoFields.size() == 65535)
41 : {
42 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too many fields");
43 0 : return false;
44 : }
45 :
46 13101 : if (psField->GetType() == FGFT_RASTER)
47 : {
48 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unhandled field type");
49 0 : return false;
50 : }
51 :
52 13101 : if (GetFieldIdx(psField->GetName()) >= 0)
53 : {
54 2 : CPLError(CE_Failure, CPLE_NotSupported, "Field %s already exists",
55 2 : psField->GetName().c_str());
56 2 : return false;
57 : }
58 :
59 13099 : if (psField->GetType() == FGFT_GEOMETRY)
60 : {
61 422 : if (m_iGeomField >= 0)
62 : {
63 0 : CPLError(CE_Failure, CPLE_NotSupported,
64 : "Only one geometry field supported");
65 0 : return false;
66 : }
67 422 : m_iGeomField = static_cast<int>(m_apoFields.size());
68 : m_adfSpatialIndexGridResolution =
69 : cpl::down_cast<const FileGDBGeomField *>(psField.get())
70 422 : ->GetSpatialIndexGridResolution();
71 : }
72 :
73 13099 : if (psField->GetType() == FGFT_OBJECTID)
74 : {
75 1635 : if (m_iObjectIdField >= 0)
76 : {
77 0 : CPLError(CE_Failure, CPLE_NotSupported,
78 : "Only one ObjectId field supported");
79 0 : return false;
80 : }
81 1635 : m_iObjectIdField = static_cast<int>(m_apoFields.size());
82 : }
83 :
84 13099 : bool bRewriteTable = false;
85 13099 : if (m_nTotalRecordCount != 0)
86 : {
87 134 : const bool bHasDefault = !OGR_RawField_IsNull(psField->GetDefault()) &&
88 67 : !OGR_RawField_IsUnset(psField->GetDefault());
89 67 : if (psField->GetType() == FGFT_GEOMETRY)
90 : {
91 0 : CPLError(CE_Failure, CPLE_NotSupported,
92 : "Cannot add a geometry field to a non-empty table");
93 0 : return false;
94 : }
95 67 : else if (psField->GetType() == FGFT_OBJECTID)
96 : {
97 : // nothing to do but rewrite the feature definition
98 : }
99 67 : else if ((m_nCountNullableFields % 8) != 0 && psField->IsNullable())
100 : {
101 : // Adding a nullable field to a feature definition that has already
102 : // nullable fields, with the last bitmap byte not completely filled.
103 : // We just need to rewrite the feature definition, not the features.
104 : }
105 45 : else if (!psField->IsNullable() && !bHasDefault)
106 : {
107 1 : CPLError(CE_Failure, CPLE_NotSupported,
108 : "Cannot add non-nullable field without default value to "
109 : "a non-empty table");
110 1 : return false;
111 : }
112 : else
113 : {
114 44 : bRewriteTable = true;
115 : }
116 : }
117 :
118 13098 : m_nCurRow = -1;
119 13098 : m_bDirtyFieldDescriptors = true;
120 13098 : const bool bIsNullable = psField->IsNullable();
121 13098 : if (bIsNullable)
122 : {
123 7537 : m_nCountNullableFields++;
124 7537 : m_nNullableFieldsSizeInBytes =
125 7537 : BIT_ARRAY_SIZE_IN_BYTES(m_nCountNullableFields);
126 : }
127 13098 : psField->SetParent(this);
128 13098 : m_apoFields.emplace_back(std::move(psField));
129 :
130 13098 : if (bRewriteTable && !RewriteTableToAddLastAddedField())
131 : {
132 6 : if (bIsNullable)
133 : {
134 0 : m_nCountNullableFields--;
135 0 : m_nNullableFieldsSizeInBytes =
136 0 : BIT_ARRAY_SIZE_IN_BYTES(m_nCountNullableFields);
137 : }
138 6 : m_apoFields.pop_back();
139 6 : m_bDirtyFieldDescriptors = true;
140 6 : return false;
141 : }
142 :
143 13092 : return true;
144 : }
145 :
146 : /************************************************************************/
147 : /* RewriteTableToAddLastAddedField() */
148 : /************************************************************************/
149 :
150 44 : bool FileGDBTable::RewriteTableToAddLastAddedField()
151 : {
152 44 : int nOldCountNullableFields = m_nCountNullableFields;
153 44 : if (m_apoFields.back()->IsNullable())
154 : {
155 2 : nOldCountNullableFields--;
156 : }
157 44 : const unsigned nOldNullableFieldsSizeInBytes =
158 44 : BIT_ARRAY_SIZE_IN_BYTES(nOldCountNullableFields);
159 44 : int nExtraBytes = 0;
160 44 : if (nOldNullableFieldsSizeInBytes != m_nNullableFieldsSizeInBytes)
161 2 : nExtraBytes++;
162 88 : std::vector<GByte> abyDefaultVal;
163 :
164 44 : const auto &psLastField = m_apoFields.back();
165 44 : if (!psLastField->IsNullable())
166 : {
167 : const bool bHasDefault =
168 84 : !OGR_RawField_IsNull(psLastField->GetDefault()) &&
169 42 : !OGR_RawField_IsUnset(psLastField->GetDefault());
170 42 : CPL_IGNORE_RET_VAL(bHasDefault);
171 42 : CPLAssert(bHasDefault);
172 42 : if (psLastField->GetType() == FGFT_STRING)
173 : {
174 12 : const std::string osDefaultVal(psLastField->GetDefault()->String);
175 12 : WriteVarUInt(abyDefaultVal, osDefaultVal.size());
176 : abyDefaultVal.insert(
177 12 : abyDefaultVal.end(),
178 12 : reinterpret_cast<const GByte *>(osDefaultVal.c_str()),
179 12 : reinterpret_cast<const GByte *>(osDefaultVal.c_str()) +
180 24 : osDefaultVal.size());
181 : }
182 30 : else if (psLastField->GetType() == FGFT_INT16)
183 : {
184 6 : WriteInt16(abyDefaultVal, static_cast<int16_t>(
185 6 : psLastField->GetDefault()->Integer));
186 : }
187 24 : else if (psLastField->GetType() == FGFT_INT32)
188 : {
189 6 : WriteInt32(abyDefaultVal, psLastField->GetDefault()->Integer);
190 : }
191 18 : else if (psLastField->GetType() == FGFT_INT64)
192 : {
193 0 : WriteInt64(abyDefaultVal, psLastField->GetDefault()->Integer64);
194 : }
195 18 : else if (psLastField->GetType() == FGFT_FLOAT32)
196 : {
197 6 : WriteFloat32(abyDefaultVal,
198 6 : static_cast<float>(psLastField->GetDefault()->Real));
199 : }
200 12 : else if (psLastField->GetType() == FGFT_FLOAT64)
201 : {
202 6 : WriteFloat64(abyDefaultVal, psLastField->GetDefault()->Real);
203 : }
204 6 : else if (psLastField->GetType() == FGFT_DATETIME ||
205 0 : psLastField->GetType() == FGFT_DATE)
206 : {
207 6 : WriteFloat64(abyDefaultVal, FileGDBOGRDateToDoubleDate(
208 : psLastField->GetDefault(),
209 : /* bConvertToUTC = */ true,
210 6 : psLastField->IsHighPrecision()));
211 : }
212 0 : else if (psLastField->GetType() == FGFT_TIME)
213 : {
214 0 : WriteFloat64(abyDefaultVal,
215 : FileGDBOGRTimeToDoubleTime(psLastField->GetDefault()));
216 : }
217 0 : else if (psLastField->GetType() == FGFT_DATETIME_WITH_OFFSET)
218 : {
219 0 : const auto psDefault = psLastField->GetDefault();
220 0 : WriteFloat64(abyDefaultVal,
221 : FileGDBOGRDateToDoubleDate(
222 : psDefault, /* bConvertToUTC = */ false,
223 : /* bIsHighPrecision= */ true));
224 0 : if (psDefault->Date.TZFlag > 1)
225 : {
226 0 : WriteInt16(
227 : abyDefaultVal,
228 0 : static_cast<int16_t>((psDefault->Date.TZFlag - 100) * 15));
229 : }
230 : else
231 : {
232 0 : WriteInt16(abyDefaultVal, 0);
233 : }
234 : }
235 42 : nExtraBytes += static_cast<int>(abyDefaultVal.size());
236 : }
237 44 : CPLAssert(nExtraBytes != 0);
238 :
239 88 : std::vector<GByte> abyBufferOffsets;
240 44 : abyBufferOffsets.resize(1024 * m_nTablxOffsetSize);
241 :
242 88 : WholeFileRewriter oWholeFileRewriter(*this);
243 44 : if (!oWholeFileRewriter.Begin())
244 0 : return false;
245 :
246 44 : if (CPLTestBool(CPLGetConfigOption(
247 : "OPENFILEGDB_SIMUL_ERROR_IN_RewriteTableToAddLastAddedField",
248 : "FALSE")))
249 : {
250 6 : return false;
251 : }
252 :
253 38 : uint32_t nRowBufferMaxSize = 0;
254 38 : m_nCurRow = -1;
255 :
256 : // Rewrite all features
257 76 : for (uint32_t iPage = 0; iPage < m_n1024BlocksPresent; ++iPage)
258 : {
259 38 : const vsi_l_offset nOffsetInTableX =
260 38 : 16 + m_nTablxOffsetSize * static_cast<vsi_l_offset>(iPage) * 1024;
261 38 : VSIFSeekL(oWholeFileRewriter.m_fpOldGdbtablx, nOffsetInTableX,
262 : SEEK_SET);
263 38 : if (VSIFReadL(abyBufferOffsets.data(), m_nTablxOffsetSize * 1024, 1,
264 38 : oWholeFileRewriter.m_fpOldGdbtablx) != 1)
265 0 : return false;
266 :
267 38 : GByte *pabyBufferOffsets = abyBufferOffsets.data();
268 38950 : for (int i = 0; i < 1024; i++, pabyBufferOffsets += m_nTablxOffsetSize)
269 : {
270 38912 : const uint64_t nOffset = ReadFeatureOffset(pabyBufferOffsets);
271 38912 : if (nOffset != 0)
272 : {
273 : // Read feature size
274 77 : VSIFSeekL(oWholeFileRewriter.m_fpOldGdbtable, nOffset,
275 : SEEK_SET);
276 77 : uint32_t nFeatureSize = 0;
277 77 : if (!ReadUInt32(oWholeFileRewriter.m_fpOldGdbtable,
278 : nFeatureSize))
279 0 : return false;
280 :
281 : // Read feature data
282 77 : if (nFeatureSize > m_abyBuffer.size())
283 : {
284 : try
285 : {
286 18 : m_abyBuffer.resize(nFeatureSize);
287 : }
288 0 : catch (const std::exception &e)
289 : {
290 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
291 0 : return false;
292 : }
293 : }
294 77 : if (VSIFReadL(m_abyBuffer.data(), nFeatureSize, 1,
295 77 : oWholeFileRewriter.m_fpOldGdbtable) != 1)
296 0 : return false;
297 :
298 : // Update offset of updated feature
299 77 : WriteFeatureOffset(m_nFileSize, pabyBufferOffsets);
300 :
301 : // Write updated feature size
302 77 : const uint32_t nNewFeatureSize = nFeatureSize + nExtraBytes;
303 77 : if (!WriteUInt32(oWholeFileRewriter.m_fpTable, nNewFeatureSize))
304 0 : return false;
305 :
306 : // Write updated feature data
307 77 : if (nOldNullableFieldsSizeInBytes != 0)
308 : {
309 77 : if (VSIFWriteL(m_abyBuffer.data(),
310 : nOldNullableFieldsSizeInBytes, 1,
311 77 : oWholeFileRewriter.m_fpTable) != 1)
312 0 : return false;
313 : }
314 77 : if (nOldNullableFieldsSizeInBytes !=
315 77 : m_nNullableFieldsSizeInBytes)
316 : {
317 5 : CPLAssert(psLastField->IsNullable());
318 5 : const GByte byNewNullableFieldByte = 0xFF;
319 5 : if (VSIFWriteL(&byNewNullableFieldByte, 1, 1,
320 5 : oWholeFileRewriter.m_fpTable) != 1)
321 0 : return false;
322 : }
323 77 : if (nFeatureSize > nOldNullableFieldsSizeInBytes)
324 : {
325 231 : if (VSIFWriteL(m_abyBuffer.data() +
326 77 : nOldNullableFieldsSizeInBytes,
327 77 : nFeatureSize - nOldNullableFieldsSizeInBytes,
328 77 : 1, oWholeFileRewriter.m_fpTable) != 1)
329 0 : return false;
330 : }
331 77 : if (!abyDefaultVal.empty())
332 : {
333 72 : if (VSIFWriteL(abyDefaultVal.data(), abyDefaultVal.size(),
334 72 : 1, oWholeFileRewriter.m_fpTable) != 1)
335 0 : return false;
336 : }
337 :
338 77 : if (nNewFeatureSize > nRowBufferMaxSize)
339 40 : nRowBufferMaxSize = nNewFeatureSize;
340 77 : m_nFileSize += sizeof(uint32_t) + nNewFeatureSize;
341 : }
342 : }
343 38 : VSIFSeekL(oWholeFileRewriter.m_fpTableX, nOffsetInTableX, SEEK_SET);
344 38 : if (VSIFWriteL(abyBufferOffsets.data(), m_nTablxOffsetSize * 1024, 1,
345 38 : oWholeFileRewriter.m_fpTableX) != 1)
346 0 : return false;
347 : }
348 :
349 38 : m_nRowBufferMaxSize = nRowBufferMaxSize;
350 38 : m_nHeaderBufferMaxSize = std::max(m_nFieldDescLength, m_nRowBufferMaxSize);
351 :
352 38 : return oWholeFileRewriter.Commit();
353 : }
354 :
355 : /************************************************************************/
356 : /* WriteFieldDescriptor() */
357 : /************************************************************************/
358 :
359 : static void
360 13766 : WriteFieldDescriptor(std::vector<GByte> &abyBuffer, const FileGDBField *psField,
361 : bool bGeomTypeHasZ, bool bGeomTypeHasM,
362 : bool bStringsAreUTF8, uint32_t &nGeomFieldBBoxOffsetOut,
363 : uint32_t &nGeomFieldSpatialIndexGridResOffsetOut)
364 : {
365 13766 : WriteUTF16String(abyBuffer, psField->GetName().c_str(),
366 : NUMBER_OF_CHARS_ON_UINT8);
367 13766 : WriteUTF16String(abyBuffer, psField->GetAlias().c_str(),
368 : NUMBER_OF_CHARS_ON_UINT8);
369 13766 : WriteUInt8(abyBuffer, static_cast<uint8_t>(psField->GetType()));
370 :
371 13766 : const auto &sDefault = *(psField->GetDefault());
372 :
373 13766 : uint8_t nFlag = 0;
374 13766 : if (psField->IsNullable())
375 7817 : nFlag = static_cast<uint8_t>(nFlag | FileGDBField::MASK_NULLABLE);
376 13766 : if (psField->IsRequired())
377 2764 : nFlag = static_cast<uint8_t>(nFlag | FileGDBField::MASK_REQUIRED);
378 13766 : if (psField->IsEditable())
379 11528 : nFlag = static_cast<uint8_t>(nFlag | FileGDBField::MASK_EDITABLE);
380 :
381 13766 : switch (psField->GetType())
382 : {
383 0 : case FGFT_UNDEFINED:
384 : {
385 0 : CPLAssert(false);
386 : break;
387 : }
388 :
389 303 : case FGFT_INT16:
390 : {
391 303 : WriteUInt8(abyBuffer, 2); // sizeof(int16)
392 303 : WriteUInt8(abyBuffer, nFlag);
393 606 : if (!OGR_RawField_IsNull(&sDefault) &&
394 303 : !OGR_RawField_IsUnset(&sDefault))
395 : {
396 66 : WriteUInt8(abyBuffer, 2); // sizeof(int16)
397 66 : WriteInt16(abyBuffer, static_cast<int16_t>(sDefault.Integer));
398 : }
399 : else
400 : {
401 237 : WriteUInt8(abyBuffer, 0); // size of default value
402 : }
403 303 : break;
404 : }
405 :
406 1296 : case FGFT_INT32:
407 : {
408 1296 : WriteUInt8(abyBuffer, 4); // sizeof(int32)
409 1296 : WriteUInt8(abyBuffer, nFlag);
410 2592 : if (!OGR_RawField_IsNull(&sDefault) &&
411 1296 : !OGR_RawField_IsUnset(&sDefault))
412 : {
413 58 : WriteUInt8(abyBuffer, 4); // sizeof(int32)
414 58 : WriteInt32(abyBuffer, sDefault.Integer);
415 : }
416 : else
417 : {
418 1238 : WriteUInt8(abyBuffer, 0); // size of default value
419 : }
420 1296 : break;
421 : }
422 :
423 49 : case FGFT_FLOAT32:
424 : {
425 49 : WriteUInt8(abyBuffer, 4); // sizeof(float32)
426 49 : WriteUInt8(abyBuffer, nFlag);
427 98 : if (!OGR_RawField_IsNull(&sDefault) &&
428 49 : !OGR_RawField_IsUnset(&sDefault))
429 : {
430 42 : WriteUInt8(abyBuffer, 4); // sizeof(float32)
431 42 : WriteFloat32(abyBuffer, static_cast<float>(sDefault.Real));
432 : }
433 : else
434 : {
435 7 : WriteUInt8(abyBuffer, 0); // size of default value
436 : }
437 49 : break;
438 : }
439 :
440 2342 : case FGFT_FLOAT64:
441 : {
442 2342 : WriteUInt8(abyBuffer, 8); // sizeof(float64)
443 2342 : WriteUInt8(abyBuffer, nFlag);
444 4684 : if (!OGR_RawField_IsNull(&sDefault) &&
445 2342 : !OGR_RawField_IsUnset(&sDefault))
446 : {
447 32 : WriteUInt8(abyBuffer, 8); // sizeof(float64)
448 32 : WriteFloat64(abyBuffer, sDefault.Real);
449 : }
450 : else
451 : {
452 2310 : WriteUInt8(abyBuffer, 0); // size of default value
453 : }
454 2342 : break;
455 : }
456 :
457 3746 : case FGFT_STRING:
458 : {
459 3746 : WriteUInt32(abyBuffer, psField->GetMaxWidth());
460 3746 : WriteUInt8(abyBuffer, nFlag);
461 7492 : if (!OGR_RawField_IsNull(&sDefault) &&
462 3746 : !OGR_RawField_IsUnset(&sDefault))
463 : {
464 95 : if (bStringsAreUTF8)
465 : {
466 94 : const auto nLen = strlen(sDefault.String);
467 94 : WriteVarUInt(abyBuffer, nLen);
468 94 : if (nLen > 0)
469 : {
470 0 : abyBuffer.insert(abyBuffer.end(), sDefault.String,
471 94 : sDefault.String + nLen);
472 : }
473 : }
474 : else
475 : {
476 1 : WriteUTF16String(abyBuffer, sDefault.String,
477 : NUMBER_OF_BYTES_ON_VARUINT);
478 : }
479 : }
480 : else
481 : {
482 3651 : WriteUInt8(abyBuffer, 0); // size of default value
483 : }
484 3746 : break;
485 : }
486 :
487 61 : case FGFT_DATETIME:
488 : case FGFT_DATE:
489 : {
490 61 : WriteUInt8(abyBuffer, 8); // sizeof(float64)
491 61 : WriteUInt8(abyBuffer, nFlag);
492 122 : if (!OGR_RawField_IsNull(&sDefault) &&
493 61 : !OGR_RawField_IsUnset(&sDefault))
494 : {
495 20 : WriteUInt8(abyBuffer, 8); // sizeof(float64)
496 20 : WriteFloat64(abyBuffer,
497 : FileGDBOGRDateToDoubleDate(
498 : &sDefault, /* bConvertToUTC = */ true,
499 20 : psField->IsHighPrecision()));
500 : }
501 : else
502 : {
503 41 : WriteUInt8(abyBuffer, 0); // size of default value
504 : }
505 61 : break;
506 : }
507 :
508 1762 : case FGFT_OBJECTID:
509 : {
510 1762 : WriteUInt8(abyBuffer, 4); // sizeof(uint32) ?
511 1762 : WriteUInt8(abyBuffer, 2); // magic value
512 1762 : break;
513 : }
514 :
515 544 : case FGFT_GEOMETRY:
516 : {
517 : const auto *geomField =
518 544 : cpl::down_cast<const FileGDBGeomField *>(psField);
519 544 : WriteUInt8(abyBuffer, 0); // unknown role
520 544 : WriteUInt8(abyBuffer, nFlag);
521 544 : WriteUTF16String(abyBuffer, geomField->GetWKT().c_str(),
522 : NUMBER_OF_BYTES_ON_UINT16);
523 544 : WriteUInt8(
524 : abyBuffer,
525 : static_cast<uint8_t>(
526 : 1 |
527 1088 : (((geomField->HasMOriginScaleTolerance() ? 1 : 0)) << 1) |
528 544 : (((geomField->HasZOriginScaleTolerance() ? 1 : 0)) << 2)));
529 544 : WriteFloat64(abyBuffer, geomField->GetXOrigin());
530 544 : WriteFloat64(abyBuffer, geomField->GetYOrigin());
531 544 : WriteFloat64(abyBuffer, geomField->GetXYScale());
532 544 : if (geomField->HasMOriginScaleTolerance())
533 : {
534 544 : WriteFloat64(abyBuffer, geomField->GetMOrigin());
535 544 : WriteFloat64(abyBuffer, geomField->GetMScale());
536 : }
537 544 : if (geomField->HasZOriginScaleTolerance())
538 : {
539 544 : WriteFloat64(abyBuffer, geomField->GetZOrigin());
540 544 : WriteFloat64(abyBuffer, geomField->GetZScale());
541 : }
542 544 : WriteFloat64(abyBuffer, geomField->GetXYTolerance());
543 544 : if (geomField->HasMOriginScaleTolerance())
544 : {
545 544 : WriteFloat64(abyBuffer, geomField->GetMTolerance());
546 : }
547 544 : if (geomField->HasZOriginScaleTolerance())
548 : {
549 544 : WriteFloat64(abyBuffer, geomField->GetZTolerance());
550 : }
551 544 : nGeomFieldBBoxOffsetOut = static_cast<uint32_t>(abyBuffer.size());
552 544 : WriteFloat64(abyBuffer, geomField->GetXMin());
553 544 : WriteFloat64(abyBuffer, geomField->GetYMin());
554 544 : WriteFloat64(abyBuffer, geomField->GetXMax());
555 544 : WriteFloat64(abyBuffer, geomField->GetYMax());
556 544 : if (bGeomTypeHasZ)
557 : {
558 48 : WriteFloat64(abyBuffer, geomField->GetZMin());
559 48 : WriteFloat64(abyBuffer, geomField->GetZMax());
560 : }
561 544 : if (bGeomTypeHasM)
562 : {
563 24 : WriteFloat64(abyBuffer, geomField->GetMMin());
564 24 : WriteFloat64(abyBuffer, geomField->GetMMax());
565 : }
566 544 : WriteUInt8(abyBuffer, 0); // possibly an indicator of existence of
567 : // spatial index or its type?
568 : const auto &adfSpatialIndexGridResolution =
569 544 : geomField->GetSpatialIndexGridResolution();
570 544 : WriteUInt32(abyBuffer, static_cast<uint32_t>(
571 544 : adfSpatialIndexGridResolution.size()));
572 544 : nGeomFieldSpatialIndexGridResOffsetOut =
573 544 : static_cast<uint32_t>(abyBuffer.size());
574 1544 : for (double dfSize : adfSpatialIndexGridResolution)
575 1000 : WriteFloat64(abyBuffer, dfSize);
576 544 : break;
577 : }
578 :
579 230 : case FGFT_BINARY:
580 : {
581 230 : WriteUInt8(abyBuffer, 0); // unknown role
582 230 : WriteUInt8(abyBuffer, nFlag);
583 230 : break;
584 : }
585 :
586 0 : case FGFT_RASTER:
587 : {
588 : // Not handled for now
589 0 : CPLAssert(false);
590 : break;
591 : }
592 :
593 2512 : case FGFT_GUID:
594 : case FGFT_GLOBALID:
595 : {
596 2512 : WriteUInt8(abyBuffer, 38); // size
597 2512 : WriteUInt8(abyBuffer, nFlag);
598 2512 : break;
599 : }
600 :
601 914 : case FGFT_XML:
602 : {
603 914 : WriteUInt8(abyBuffer, 0); // unknown role
604 914 : WriteUInt8(abyBuffer, nFlag);
605 914 : break;
606 : }
607 :
608 1 : case FGFT_INT64:
609 : {
610 1 : WriteUInt8(abyBuffer, 8); // sizeof(int64)
611 1 : WriteUInt8(abyBuffer, nFlag);
612 2 : if (!OGR_RawField_IsNull(&sDefault) &&
613 1 : !OGR_RawField_IsUnset(&sDefault))
614 : {
615 1 : WriteUInt8(abyBuffer, 8); // sizeof(int64)
616 1 : WriteInt64(abyBuffer, sDefault.Integer64);
617 : }
618 : else
619 : {
620 0 : WriteUInt8(abyBuffer, 0); // size of default value
621 : }
622 1 : break;
623 : }
624 :
625 2 : case FGFT_TIME:
626 : {
627 2 : WriteUInt8(abyBuffer, 8); // sizeof(float64)
628 2 : WriteUInt8(abyBuffer, nFlag);
629 4 : if (!OGR_RawField_IsNull(&sDefault) &&
630 2 : !OGR_RawField_IsUnset(&sDefault))
631 : {
632 2 : WriteUInt8(abyBuffer, 8); // sizeof(float64)
633 2 : WriteFloat64(abyBuffer, FileGDBOGRTimeToDoubleTime(&sDefault));
634 : }
635 : else
636 : {
637 0 : WriteUInt8(abyBuffer, 0); // size of default value
638 : }
639 2 : break;
640 : }
641 :
642 4 : case FGFT_DATETIME_WITH_OFFSET:
643 : {
644 4 : WriteUInt8(abyBuffer, 8 + 2); // sizeof(float64) + sizeof(int16)
645 4 : WriteUInt8(abyBuffer, nFlag);
646 8 : if (!OGR_RawField_IsNull(&sDefault) &&
647 4 : !OGR_RawField_IsUnset(&sDefault))
648 : {
649 4 : WriteUInt8(abyBuffer,
650 : 8 + 2); // sizeof(float64) + sizeof(int16)
651 4 : WriteFloat64(abyBuffer,
652 : FileGDBOGRDateToDoubleDate(
653 : &sDefault, /* bConvertToUTC = */ false,
654 : /* bIsHighPrecision = */ true));
655 4 : if (sDefault.Date.TZFlag > 1)
656 : {
657 2 : WriteInt16(abyBuffer,
658 : static_cast<int16_t>(
659 2 : (sDefault.Date.TZFlag - 100) * 15));
660 : }
661 : else
662 : {
663 2 : WriteInt16(abyBuffer, 0);
664 : }
665 : }
666 : else
667 : {
668 0 : WriteUInt8(abyBuffer, 0); // size of default value
669 : }
670 4 : break;
671 : }
672 : }
673 13766 : }
674 :
675 : /************************************************************************/
676 : /* WriteFieldDescriptors() */
677 : /************************************************************************/
678 :
679 1991 : bool FileGDBTable::WriteFieldDescriptors(VSILFILE *fpTable)
680 : {
681 1991 : m_bDirtyFieldDescriptors = false;
682 :
683 : // In-memory field descriptors
684 3982 : std::vector<GByte> abyBuffer;
685 :
686 1991 : WriteUInt32(abyBuffer, 0); // size of field section, excluding this field.
687 : // Will be patched later
688 1991 : WriteUInt32(abyBuffer, 4); // version of the file
689 :
690 1991 : const uint32_t nLayerFlags =
691 3982 : static_cast<uint32_t>(m_eTableGeomType) |
692 1991 : ((m_bStringsAreUTF8 ? 1 : 0) << 8) | // string encoding
693 1991 : (((m_eTableGeomType != FGTGT_NONE) ? 1 : 0)
694 1991 : << 9) | // "high precision storage"
695 1991 : (static_cast<uint32_t>(m_bGeomTypeHasM) << 30U) |
696 1991 : (static_cast<uint32_t>(m_bGeomTypeHasZ) << 31U);
697 1991 : WriteUInt32(abyBuffer, nLayerFlags);
698 :
699 1991 : WriteUInt16(abyBuffer, static_cast<uint16_t>(m_apoFields.size()));
700 :
701 1991 : m_nGeomFieldBBoxSubOffset = 0;
702 15757 : for (const auto &poField : m_apoFields)
703 : {
704 13766 : WriteFieldDescriptor(abyBuffer, poField.get(), m_bGeomTypeHasZ,
705 13766 : m_bGeomTypeHasM, m_bStringsAreUTF8,
706 13766 : m_nGeomFieldBBoxSubOffset,
707 13766 : m_nGeomFieldSpatialIndexGridResSubOffset);
708 : }
709 :
710 : // Just to imitate the behavior of the FileGDB SDK !
711 1991 : abyBuffer.push_back(0xDE);
712 1991 : abyBuffer.push_back(0xAD);
713 1991 : abyBuffer.push_back(0xBE);
714 1991 : abyBuffer.push_back(0xEF);
715 :
716 : // Patch size of field section at beginning of buffer
717 : const auto nFieldSectionSize =
718 1991 : static_cast<uint32_t>(abyBuffer.size() - sizeof(uint32_t));
719 1991 : WriteUInt32(abyBuffer, nFieldSectionSize, 0);
720 :
721 1991 : bool bUpdateFileSize = false;
722 1991 : const auto nOldFieldDescLength = m_nFieldDescLength;
723 1991 : if (m_nOffsetFieldDesc + m_nFieldDescLength == m_nFileSize)
724 : {
725 : // Optimization: if the field descriptor section is already at end of
726 : // file, we can rewrite-in-place whatever its new size
727 0 : VSIFSeekL(fpTable, m_nOffsetFieldDesc, SEEK_SET);
728 0 : bUpdateFileSize = true;
729 : }
730 1991 : else if (abyBuffer.size() > m_nFieldDescLength)
731 : {
732 1971 : if (m_nOffsetFieldDesc != 0)
733 : {
734 62 : VSIFSeekL(fpTable, m_nOffsetFieldDesc, SEEK_SET);
735 : // Cancel unused bytes with NUL characters
736 62 : std::vector<GByte> abyNul(m_nFieldDescLength + sizeof(uint32_t));
737 62 : CPL_IGNORE_RET_VAL(
738 62 : VSIFWriteL(abyNul.data(), 1, abyNul.size(), fpTable));
739 : }
740 1971 : VSIFSeekL(fpTable, m_nFileSize, SEEK_SET);
741 1971 : m_bDirtyHeader = true;
742 1971 : m_nOffsetFieldDesc = m_nFileSize;
743 1971 : m_nFileSize += abyBuffer.size();
744 : }
745 : else
746 : {
747 20 : VSIFSeekL(fpTable, m_nOffsetFieldDesc, SEEK_SET);
748 : }
749 :
750 : // Write new field descriptor
751 1991 : m_nFieldDescLength = nFieldSectionSize;
752 3982 : if (VSIFWriteL(abyBuffer.data(), 1, abyBuffer.size(), fpTable) !=
753 1991 : abyBuffer.size())
754 0 : return false;
755 :
756 1991 : if (bUpdateFileSize)
757 : {
758 0 : m_nFileSize = VSIFTellL(fpTable);
759 0 : VSIFTruncateL(fpTable, m_nFileSize);
760 0 : m_bDirtyHeader = true;
761 : }
762 1991 : else if (nOldFieldDescLength != 0 &&
763 82 : m_nFieldDescLength < nOldFieldDescLength)
764 : {
765 : // Cancel unused bytes with NUL characters
766 20 : std::vector<GByte> abyNul(nOldFieldDescLength - m_nFieldDescLength);
767 20 : CPL_IGNORE_RET_VAL(
768 20 : VSIFWriteL(abyNul.data(), 1, abyNul.size(), fpTable));
769 : }
770 :
771 1991 : return true;
772 : }
773 :
774 : /************************************************************************/
775 : /* DeleteField() */
776 : /************************************************************************/
777 :
778 14 : bool FileGDBTable::DeleteField(int iField)
779 : {
780 14 : if (!m_bUpdate)
781 0 : return false;
782 :
783 14 : if (iField < 0 || iField >= static_cast<int>(m_apoFields.size()))
784 : {
785 0 : return false;
786 : }
787 :
788 14 : if (m_iGeomField == iField)
789 : {
790 0 : CPLError(CE_Failure, CPLE_NotSupported,
791 : "Geometry field deletion not supported");
792 0 : return false;
793 : }
794 :
795 14 : bool bRet = true;
796 14 : if (iField != m_iObjectIdField)
797 : {
798 14 : std::vector<GByte> abyBlank;
799 :
800 : // Little hack: we present the geometry field as a binary one
801 : // to avoid any conversion
802 14 : const int iGeomFieldBackup = m_iGeomField;
803 14 : if (m_iGeomField >= 0)
804 12 : m_apoFields[m_iGeomField]->m_eType = FGFT_BINARY;
805 14 : m_iGeomField = -1;
806 :
807 40 : for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat)
808 : {
809 26 : iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat);
810 26 : if (iCurFeat < 0)
811 0 : break;
812 52 : auto asValues = GetAllFieldValues();
813 :
814 26 : if (m_nRowBlobLength > 0)
815 : {
816 26 : if (EncodeFeature(asValues, nullptr, iField))
817 : {
818 26 : VSIFSeekL(m_fpTable,
819 26 : VSIFTellL(m_fpTable) - sizeof(uint32_t) -
820 26 : m_nRowBlobLength,
821 : SEEK_SET);
822 :
823 26 : abyBlank.resize(m_nRowBlobLength - m_abyBuffer.size());
824 :
825 26 : if (!WriteUInt32(m_fpTable, static_cast<uint32_t>(
826 26 : m_abyBuffer.size())) ||
827 26 : VSIFWriteL(m_abyBuffer.data(), m_abyBuffer.size(), 1,
828 52 : m_fpTable) != 1 ||
829 26 : (!abyBlank.empty() &&
830 21 : VSIFWriteL(abyBlank.data(), abyBlank.size(), 1,
831 : m_fpTable) != 1))
832 : {
833 0 : bRet = false;
834 : }
835 : }
836 : else
837 : {
838 0 : bRet = false;
839 : }
840 : }
841 :
842 26 : FreeAllFieldValues(asValues);
843 : }
844 :
845 14 : if (iGeomFieldBackup >= 0)
846 12 : m_apoFields[iGeomFieldBackup]->m_eType = FGFT_GEOMETRY;
847 14 : m_iGeomField = iGeomFieldBackup;
848 : }
849 :
850 : // Delete linked index if existing
851 14 : GetIndexCount();
852 14 : if (m_apoFields[iField]->m_poIndex)
853 : {
854 4 : for (size_t i = 0; i < m_apoIndexes.size(); ++i)
855 : {
856 4 : if (m_apoIndexes[i].get() == m_apoFields[iField]->m_poIndex)
857 : {
858 1 : m_bDirtyGdbIndexesFile = true;
859 :
860 1 : if (iField != m_iObjectIdField)
861 : {
862 1 : VSIUnlink(CPLResetExtension(
863 : m_osFilename.c_str(),
864 2 : (m_apoIndexes[i]->GetIndexName() + ".atx").c_str()));
865 : }
866 :
867 1 : m_apoIndexes.erase(m_apoIndexes.begin() + i);
868 1 : break;
869 : }
870 : }
871 : }
872 :
873 : // Renumber objectId and geomField indices
874 14 : if (m_iObjectIdField == iField)
875 0 : m_iObjectIdField = -1;
876 14 : else if (iField < m_iObjectIdField)
877 2 : m_iObjectIdField--;
878 :
879 14 : if (iField < m_iGeomField)
880 2 : m_iGeomField--;
881 :
882 14 : if (m_apoFields[iField]->IsNullable())
883 : {
884 12 : m_nCountNullableFields--;
885 12 : m_nNullableFieldsSizeInBytes =
886 12 : BIT_ARRAY_SIZE_IN_BYTES(m_nCountNullableFields);
887 : }
888 :
889 14 : m_apoFields.erase(m_apoFields.begin() + iField);
890 :
891 14 : m_bDirtyFieldDescriptors = true;
892 :
893 14 : return bRet;
894 : }
895 :
896 : /************************************************************************/
897 : /* AlterField() */
898 : /************************************************************************/
899 :
900 7 : bool FileGDBTable::AlterField(int iField, const std::string &osName,
901 : const std::string &osAlias,
902 : FileGDBFieldType eType, bool bNullable,
903 : int nMaxWidth, const OGRField &sDefault)
904 : {
905 7 : if (!m_bUpdate)
906 0 : return false;
907 :
908 7 : if (iField < 0 || iField >= static_cast<int>(m_apoFields.size()))
909 : {
910 0 : return false;
911 : }
912 :
913 7 : if (m_iGeomField == iField)
914 : {
915 0 : CPLError(CE_Failure, CPLE_NotSupported,
916 : "AlterField() not supported on geometry field");
917 0 : return false;
918 : }
919 :
920 7 : if (m_apoFields[iField]->GetType() != eType)
921 : {
922 0 : CPLError(CE_Failure, CPLE_NotSupported,
923 : "AlterField() does not support modifying the field type");
924 0 : return false;
925 : }
926 :
927 7 : if (m_apoFields[iField]->IsNullable() != bNullable)
928 : {
929 0 : CPLError(CE_Failure, CPLE_NotSupported,
930 : "AlterField() does not support modifying the nullable state");
931 0 : return false;
932 : }
933 :
934 7 : const bool bRenameField = m_apoFields[iField]->GetName() != osName;
935 7 : if (bRenameField && GetFieldIdx(osName) >= 0)
936 : {
937 1 : CPLError(
938 : CE_Failure, CPLE_NotSupported,
939 : "AlterField() cannot rename a field to an existing field name");
940 1 : return false;
941 : }
942 :
943 : // Update linked index if existing
944 6 : GetIndexCount();
945 6 : auto poIndex = m_apoFields[iField]->m_poIndex;
946 :
947 12 : m_apoFields[iField] = std::make_unique<FileGDBField>(
948 6 : osName, osAlias, eType, bNullable, m_apoFields[iField]->IsRequired(),
949 12 : m_apoFields[iField]->IsEditable(), nMaxWidth, sDefault);
950 6 : m_apoFields[iField]->SetParent(this);
951 6 : m_apoFields[iField]->m_poIndex = poIndex;
952 6 : if (poIndex && bRenameField)
953 : {
954 1 : m_bDirtyGdbIndexesFile = true;
955 1 : if (STARTS_WITH_CI(poIndex->GetExpression().c_str(), "LOWER("))
956 0 : poIndex->m_osExpression = "LOWER(" + osName + ")";
957 : else
958 1 : poIndex->m_osExpression = osName;
959 : }
960 6 : m_bDirtyFieldDescriptors = true;
961 :
962 6 : return true;
963 : }
964 :
965 : /************************************************************************/
966 : /* AlterGeomField() */
967 : /************************************************************************/
968 :
969 4 : bool FileGDBTable::AlterGeomField(const std::string &osName,
970 : const std::string &osAlias, bool bNullable,
971 : const std::string &osWKT)
972 : {
973 4 : if (!m_bUpdate)
974 0 : return false;
975 4 : if (m_iGeomField < 0)
976 0 : return false;
977 :
978 : auto poGeomField =
979 4 : cpl::down_cast<FileGDBGeomField *>(m_apoFields[m_iGeomField].get());
980 4 : if (poGeomField->IsNullable() != bNullable)
981 : {
982 0 : CPLError(
983 : CE_Failure, CPLE_NotSupported,
984 : "AlterGeomField() does not support modifying the nullable state");
985 0 : return false;
986 : }
987 :
988 4 : const bool bRenameField = poGeomField->GetName() != osName;
989 :
990 4 : poGeomField->m_osName = osName;
991 4 : poGeomField->m_osAlias = osAlias;
992 4 : poGeomField->m_bNullable = bNullable;
993 4 : poGeomField->m_osWKT = osWKT;
994 4 : auto poIndex = poGeomField->m_poIndex;
995 4 : if (poIndex && bRenameField)
996 : {
997 0 : poIndex->m_osExpression = osName;
998 0 : m_bDirtyGdbIndexesFile = true;
999 : }
1000 4 : m_bDirtyFieldDescriptors = true;
1001 :
1002 4 : return true;
1003 : }
1004 :
1005 : } /* namespace OpenFileGDB */
|