@@ -61,6 +61,86 @@ using namespace arangodb::traverser;
61
61
62
62
namespace {
63
63
64
+ struct DisjointSmartToSatelliteTester {
65
+ Result isCollectionAllowed (
66
+ std::shared_ptr<LogicalCollection> const & collection,
67
+ TRI_edge_direction_e dir) {
68
+ // We only need to check Sat -> Smart or Smart -> Sat collection, nothing
69
+ // else
70
+ if (collection->isSmartToSatEdgeCollection () ||
71
+ collection->isSatToSmartEdgeCollection ()) {
72
+ if (dir == TRI_EDGE_ANY) {
73
+ // ANY is always forbidden
74
+ return {
75
+ TRI_ERROR_UNSUPPORTED_CHANGE_IN_SMART_TO_SATELLITE_DISJOINT_EDGE_DIRECTION,
76
+ " Using direction 'ANY' on collection: '" + collection->name () +
77
+ " ' could switch from Smart to Satellite and back. This "
78
+ " violates the isDisjoint feature and is forbidden." };
79
+ }
80
+ // Unify Edge storage, and edge read direction.
81
+ // The smartToSatDir defines the direction in which we attempt to
82
+ // walk from Smart to Satellite collections. (OUT: Smart -> Sat, IN: Sat
83
+ // -> Smart)
84
+ bool isOut = dir == TRI_EDGE_OUT;
85
+ auto smartToSatDir = isOut == collection->isSmartToSatEdgeCollection ()
86
+ ? TRI_EDGE_OUT
87
+ : TRI_EDGE_IN;
88
+ if (_disjointSmartToSatDirection == TRI_EDGE_ANY) {
89
+ // We have not defined the direction yet, store it, this now defines the
90
+ // only allowed switch
91
+ _disjointSmartToSatDirection = smartToSatDir;
92
+ TRI_ASSERT (_conflictingCollection == nullptr );
93
+ _conflictingCollection = collection;
94
+ _conflictingDirection = dir;
95
+ } else if (_disjointSmartToSatDirection != smartToSatDir) {
96
+ // We try to switch again! This is disallowed. Let us report.
97
+ std::stringstream errorMessage;
98
+ errorMessage << " Using direction " ;
99
+ if (dir == TRI_EDGE_OUT) {
100
+ errorMessage << " OUTBOUND" ;
101
+ } else {
102
+ errorMessage << " INBOUND" ;
103
+ }
104
+ auto printCollection = [&errorMessage](LogicalCollection const & col,
105
+ bool isOut) {
106
+ errorMessage << " '" << col.name () << " ' switching from " ;
107
+ if (isOut == col.isSmartToSatEdgeCollection ()) {
108
+ // Hits OUTBOUND on SmartToSat and INBOUND on SatToSmart
109
+ errorMessage << " Smart to Satellite" ;
110
+ } else {
111
+ // Hits INBOUND on SmartToSat and OUTBOUND on SatToSmart
112
+ errorMessage << " Satellite to Smart" ;
113
+ }
114
+ };
115
+ errorMessage << " on collection: " ;
116
+ printCollection (*collection, isOut);
117
+
118
+ errorMessage << " . Conflicting with: " ;
119
+ if (_conflictingDirection == TRI_EDGE_OUT) {
120
+ errorMessage << " OUTBOUND" ;
121
+ } else {
122
+ errorMessage << " INBOUND" ;
123
+ }
124
+ errorMessage << " " ;
125
+ bool conflictingIsOut = _conflictingDirection == TRI_EDGE_OUT;
126
+ TRI_ASSERT (_conflictingCollection != nullptr );
127
+ printCollection (*_conflictingCollection, conflictingIsOut);
128
+ errorMessage
129
+ << " . This violates the isDisjoint feature and is forbidden." ;
130
+ return {
131
+ TRI_ERROR_UNSUPPORTED_CHANGE_IN_SMART_TO_SATELLITE_DISJOINT_EDGE_DIRECTION,
132
+ errorMessage.str ()};
133
+ }
134
+ }
135
+ return TRI_ERROR_NO_ERROR;
136
+ }
137
+
138
+ private:
139
+ TRI_edge_direction_e _disjointSmartToSatDirection{TRI_EDGE_ANY};
140
+ std::shared_ptr<LogicalCollection> _conflictingCollection{nullptr };
141
+ TRI_edge_direction_e _conflictingDirection{TRI_EDGE_ANY};
142
+ };
143
+
64
144
TRI_edge_direction_e uint64ToDirection (uint64_t dirNum) {
65
145
switch (dirNum) {
66
146
case 0 :
@@ -114,7 +194,7 @@ GraphNode::GraphNode(ExecutionPlan* plan, ExecutionNodeId id,
114
194
TRI_ASSERT (direction != nullptr );
115
195
TRI_ASSERT (graph != nullptr );
116
196
117
- auto & ci = _vocbase-> server (). getFeature <ClusterFeature>(). clusterInfo () ;
197
+ DisjointSmartToSatelliteTester disjointTest{} ;
118
198
119
199
if (graph->type == NODE_TYPE_COLLECTION_LIST) {
120
200
size_t edgeCollectionCount = graph->numMembers ();
@@ -123,36 +203,7 @@ GraphNode::GraphNode(ExecutionPlan* plan, ExecutionNodeId id,
123
203
_edgeColls.reserve (edgeCollectionCount);
124
204
_directions.reserve (edgeCollectionCount);
125
205
126
- // First determine whether all edge collections are smart and sharded
127
- // like a common collection:
128
- if (ServerState::instance ()->isRunningInCluster ()) {
129
- _isSmart = true ;
130
- _isDisjoint = true ;
131
- std::string distributeShardsLike;
132
- for (size_t i = 0 ; i < edgeCollectionCount; ++i) {
133
- auto col = graph->getMember (i);
134
- if (col->type == NODE_TYPE_DIRECTION) {
135
- col = col->getMember (1 ); // The first member always is the collection
136
- }
137
- std::string n = col->getString ();
138
- auto c = ci.getCollection (_vocbase->name (), n);
139
- if (c->isSmart () && !c->isDisjoint ()) {
140
- _isDisjoint = false ;
141
- }
142
- if (!c->isSmart () || c->distributeShardsLike ().empty ()) {
143
- _isSmart = false ;
144
- _isDisjoint = false ;
145
- break ;
146
- }
147
- if (distributeShardsLike.empty ()) {
148
- distributeShardsLike = c->distributeShardsLike ();
149
- } else if (distributeShardsLike != c->distributeShardsLike ()) {
150
- _isSmart = false ;
151
- _isDisjoint = false ;
152
- break ;
153
- }
154
- }
155
- }
206
+ determineEnterpriseFlags (graph);
156
207
157
208
std::unordered_map<std::string, TRI_edge_direction_e> seenCollections;
158
209
CollectionNameResolver const & resolver = plan->getAst ()->query ().resolver ();
@@ -199,20 +250,27 @@ GraphNode::GraphNode(ExecutionPlan* plan, ExecutionNodeId id,
199
250
THROW_ARANGO_EXCEPTION_MESSAGE (TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID,
200
251
msg);
201
252
}
253
+ if (_isDisjoint) {
254
+ // TODO: Alternative to "THROW" we could run a community based Query
255
+ // here, instead of a Disjoint one.
256
+ auto res = disjointTest.isCollectionAllowed (collection, dir);
257
+ if (res.fail ()) {
258
+ THROW_ARANGO_EXCEPTION_MESSAGE (res.errorNumber (), res.errorMessage ());
259
+ }
260
+ }
202
261
203
262
auto & collections = plan->getAst ()->query ().collections ();
204
263
205
264
_graphInfo.add (VPackValue (eColName));
206
265
if (ServerState::instance ()->isRunningInCluster ()) {
207
- auto c = ci.getCollection (_vocbase->name (), eColName);
208
- if (!c->isSmart ()) {
266
+ if (!collection->isSmart ()) {
209
267
addEdgeCollection (collections, eColName, dir);
210
268
} else {
211
269
std::vector<std::string> names;
212
270
if (_isSmart) {
213
- names = c ->realNames ();
271
+ names = collection ->realNames ();
214
272
} else {
215
- names = c ->realNamesForRead ();
273
+ names = collection ->realNamesForRead ();
216
274
}
217
275
for (auto const & name : names) {
218
276
addEdgeCollection (collections, name, dir);
@@ -242,22 +300,30 @@ GraphNode::GraphNode(ExecutionPlan* plan, ExecutionNodeId id,
242
300
THROW_ARANGO_EXCEPTION (TRI_ERROR_GRAPH_EMPTY);
243
301
}
244
302
245
- // First determine whether all edge collections are smart and sharded
246
- // like a common collection:
303
+ // Just use the Graph Object information
247
304
if (ServerState::instance ()->isRunningInCluster ()) {
248
305
_isSmart = _graphObj->isSmart ();
249
306
_isDisjoint = _graphObj->isDisjoint ();
250
307
}
251
-
308
+ auto & ci = _vocbase->server ().getFeature <ClusterFeature>().clusterInfo ();
309
+ auto & collections = plan->getAst ()->query ().collections ();
252
310
for (const auto & n : eColls) {
253
311
if (_options->shouldExcludeEdgeCollection (n)) {
254
312
// excluded edge collection
255
313
continue ;
256
314
}
257
315
258
- auto & collections = plan->getAst ()->query ().collections ();
259
316
if (ServerState::instance ()->isRunningInCluster ()) {
260
317
auto c = ci.getCollection (_vocbase->name (), n);
318
+ if (_isDisjoint) {
319
+ // TODO: Alternative to "THROW" we could run a community based Query
320
+ // here, instead of a Disjoint one.
321
+ auto res = disjointTest.isCollectionAllowed (c, _defaultDirection);
322
+ if (res.fail ()) {
323
+ THROW_ARANGO_EXCEPTION_MESSAGE (res.errorNumber (),
324
+ res.errorMessage ());
325
+ }
326
+ }
261
327
if (!c->isSmart ()) {
262
328
addEdgeCollection (collections, n, _defaultDirection);
263
329
} else {
@@ -276,7 +342,6 @@ GraphNode::GraphNode(ExecutionPlan* plan, ExecutionNodeId id,
276
342
}
277
343
}
278
344
279
- auto & collections = plan->getAst ()->query ().collections ();
280
345
auto vColls = _graphObj->vertexCollections ();
281
346
length = vColls.size ();
282
347
if (length == 0 ) {
@@ -473,6 +538,13 @@ GraphNode::GraphNode(ExecutionPlan* plan, ExecutionNodeId id,
473
538
setGraphInfoAndCopyColls (edgeColls, vertexColls);
474
539
}
475
540
541
+ #ifndef USE_ENTERPRISE
542
+ void GraphNode::determineEnterpriseFlags (AstNode const *) {
543
+ _isSmart = false ;
544
+ _isDisjoint = false ;
545
+ }
546
+ #endif
547
+
476
548
void GraphNode::setGraphInfoAndCopyColls (
477
549
std::vector<Collection*> const & edgeColls,
478
550
std::vector<Collection*> const & vertexColls) {
0 commit comments