Repository navigation
Expand file tree
/
Copy pathcorDbEntityCreate.c
More file actions
119 lines (104 loc) · 4.72 KB
/
Copy pathcorDbEntityCreate.c
File metadata and controls
119 lines (104 loc) · 4.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//
// FILE corDbEntityCreate.c
//
// AUTHOR Ken Zangelin
//
// Copyright 2026 Seamware
// SPDX-License-Identifier: Apache-2.0
//
#include <stdbool.h> // bool
#include <string.h> // strcmp
#include "corLog/corLog.h" // COR_E
#include "corTree/CorNode.h" // CorNode
#include "corTree/corTreeClone.h" // corTreeClone
#include "corTree/corTreeFree.h" // corTreeFree
#include "corTree/corTreeBuilder.h" // corTreeChildAdd
#include "corTree/corTreeLookup.h" // corTreeLookup
#include "db/DbDriver.h" // DB_OK, DB_ALREADY_EXISTS, DB_ERR, DB_INVALID_GEOMETRY, Tenant
#include "shared/geoMatch.h" // geoEntityValidate
#include "corDB/corDbSysTimes.h" // corDbTreeIn, corDbTreeOut, corDbFullView
#include "corDB/corDbIndex.h" // corDbIndexLink, corDbIndexLookup
#include "corDB/corDbPersist.h" // corDbPersistAppend
#include "corDB/corDbHistory.h" // corDbHistoryOn
#include "corDB/corDbHistoryWrite.h" // corDbHistoryCreated, ...Replaced, ...Merged, ...Deleted
#include "corRest/CorRestState.h" // corRest (kallocP - the history's scratch)
#include "corDB/corDbStore.h" // corDbEntities
#include "corDB/corDbEntityCreate.h" // Own interface
// -----------------------------------------------------------------------------
//
// corDbEntityCreate -
//
int corDbEntityCreate(Tenant* tenantP, const char* entityId, CorNode* entityP)
{
//
// The deep clone reads nothing but the request's entity, so it is done BEFORE the write lock -
// under it, it was most of the time the lock was held, and every other writer of the tenant
// waited it out (see corDbEntityBulkCreate). Not needed after all (the id exists, the geometry
// is refused): freed, outside the lock.
//
// The geometry check stays under the lock: the GEOS context (shared/geoMatch.c) is ONE for the
// process, and GEOS wants one per thread for concurrent use.
//
CorNode* cloneP = corDbTreeIn(entityP, 0); // malloc, not a buffer allocator: store lifetime
int rc = DB_OK;
//
// The record's body encoded now, before the lock (corDbPersist.h, CorDbPre) - only without --dbDir not at all
//
COR_DB_PRE(pre);
int preIx = ((cloneP != NULL) && corDbPersistOn()) ? corDbPersistPreAdd(&pre, cloneP) : -1;
COR_DB_HIST_PRE(hist); // its history records too (--troe corDB)
if ((cloneP != NULL) && corDbHistoryOn)
corDbHistoryPrepare(&hist, entityP, "created", corRest.kallocP); // the request's: every time in place
{
COR_DB_WRITE(tenantP);
CorNode* entities = corDbEntities(tenantP);
//
// Check for duplicate
//
// One hop via the id index instead of a walk of the whole store with a
// corTreeLookup per entity. The loop shape is kept so the body below is unchanged:
// indexed, it runs exactly once for the hit and not at all for a miss;
// unindexed - a store that predates the index - it walks as it always did.
//
CorDbStore* idxStoreP = corDbStoreOf(tenantP);
CorNode* idxHitP = corDbIndexLookup(idxStoreP, entityId);
bool indexed = (idxStoreP != NULL) && (idxStoreP->idToPrevEntity != NULL);
for (CorNode* eP = indexed ? idxHitP : entities->value.head;
eP != NULL;
eP = indexed ? NULL : eP->next)
{
CorNode* idP = corTreeLookup(eP, "id");
if (idP != NULL && idP->type == CorString && strcmp(idP->value.s, entityId) == 0)
{
rc = DB_ALREADY_EXISTS;
break;
}
}
if (rc == DB_OK)
{
//
// Reject geometry a 2dsphere index would refuse (degenerate / self-
// intersecting polygon). mongoc gets this from its geo index on insert;
// the in-memory store validates via the shared GEOS engine so the broker
// can map it to 400 BadRequestData instead of silently storing it.
//
if (!geoEntityValidate(entityP))
rc = DB_INVALID_GEOMETRY;
else if (cloneP == NULL)
{
COR_E("corDB: corTreeClone failed for entity '%s'", entityId);
rc = DB_ERR;
}
else
{
corDbIndexLink(corDbStoreOf(tenantP), cloneP);
corDbPersistAppendPre(corDbLockedStore->persistP, CorDbLogEntityPut, &pre, preIx, cloneP);
corDbHistoryCreatedPre(corDbLockedStore, &hist, corDbFullView(cloneP, corRest.kallocP), corRest.kallocP);
return DB_OK;
}
}
}
if (cloneP != NULL) // not stored - and freed outside the lock
corTreeFree(cloneP);
return rc;
}