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