-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
SchemaRuleSerialization.java
435 lines (376 loc) · 16.1 KB
/
SchemaRuleSerialization.java
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/*
* Copyright (c) 2002-2018 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.store.record;
import java.nio.ByteBuffer;
import org.neo4j.internal.kernel.api.schema.LabelSchemaDescriptor;
import org.neo4j.internal.kernel.api.schema.RelationTypeSchemaDescriptor;
import org.neo4j.internal.kernel.api.schema.SchemaComputer;
import org.neo4j.internal.kernel.api.schema.SchemaDescriptor;
import org.neo4j.internal.kernel.api.schema.SchemaProcessor;
import org.neo4j.internal.kernel.api.schema.constraints.ConstraintDescriptor;
import org.neo4j.kernel.api.exceptions.schema.MalformedSchemaRuleException;
import org.neo4j.kernel.api.index.IndexProvider;
import org.neo4j.kernel.api.schema.SchemaDescriptorFactory;
import org.neo4j.kernel.api.schema.constaints.ConstraintDescriptorFactory;
import org.neo4j.kernel.api.schema.constaints.NodeKeyConstraintDescriptor;
import org.neo4j.kernel.api.schema.constaints.UniquenessConstraintDescriptor;
import org.neo4j.kernel.api.schema.index.StoreIndexDescriptor;
import org.neo4j.kernel.api.schema.index.IndexDescriptor;
import org.neo4j.kernel.api.schema.index.IndexDescriptorFactory;
import org.neo4j.storageengine.api.schema.SchemaRule;
import org.neo4j.string.UTF8;
import static java.lang.String.format;
import static org.neo4j.string.UTF8.getDecodedStringFrom;
public class SchemaRuleSerialization
{
// Schema rule type
// Legacy schema store reserves 1,2,3,4 and 5
private static final byte INDEX_RULE = 11;
private static final byte CONSTRAINT_RULE = 12;
// Index type
private static final byte GENERAL_INDEX = 31;
private static final byte UNIQUE_INDEX = 32;
// Constraint type
private static final byte EXISTS_CONSTRAINT = 61;
private static final byte UNIQUE_CONSTRAINT = 62;
private static final byte UNIQUE_EXISTS_CONSTRAINT = 63;
// Schema type
private static final byte SIMPLE_LABEL = 91;
private static final byte SIMPLE_REL_TYPE = 92;
private static final long NO_OWNING_CONSTRAINT_YET = -1;
private static final int LEGACY_LABEL_OR_REL_TYPE_ID = -1;
private SchemaRuleSerialization()
{
}
// PUBLIC
/**
* Serialize the provided SchemaRule onto the target buffer
*
* @param schemaRule the SchemaRule to serialize
*/
public static byte[] serialize( SchemaRule schemaRule )
{
if ( schemaRule instanceof StoreIndexDescriptor )
{
return serialize( (StoreIndexDescriptor)schemaRule );
}
else if ( schemaRule instanceof ConstraintRule )
{
return serialize( (ConstraintRule)schemaRule );
}
throw new IllegalStateException( "Unknown schema rule type: "+schemaRule.getClass() );
}
/**
* Parse a SchemaRule from the provided buffer.
*
* @param id the id to give the returned Schema Rule
* @param source the buffer to parse from
* @return a SchemaRule
* @throws MalformedSchemaRuleException if bytes in the buffer do encode a valid SchemaRule
*/
public static SchemaRule deserialize( long id, ByteBuffer source ) throws MalformedSchemaRuleException
{
int legacyLabelOrRelTypeId = source.getInt();
byte schemaRuleType = source.get();
switch ( schemaRuleType )
{
case INDEX_RULE:
return readIndexRule( id, source );
case CONSTRAINT_RULE:
return readConstraintRule( id, source );
default:
if ( SchemaRuleDeserializer2_0to3_1.isLegacySchemaRule( schemaRuleType ) )
{
return SchemaRuleDeserializer2_0to3_1.deserialize( id, legacyLabelOrRelTypeId, schemaRuleType, source );
}
throw new MalformedSchemaRuleException( format( "Got unknown schema rule type '%d'.", schemaRuleType ) );
}
}
/**
* Serialize the provided IndexRule onto the target buffer
*
* @param indexDescriptor the StoreIndexDescriptor to serialize
* @throws IllegalStateException if the StoreIndexDescriptor is of type unique, but the owning constrain has not been set
*/
public static byte[] serialize( StoreIndexDescriptor indexDescriptor )
{
ByteBuffer target = ByteBuffer.allocate( lengthOf( indexDescriptor ) );
target.putInt( LEGACY_LABEL_OR_REL_TYPE_ID );
target.put( INDEX_RULE );
IndexProvider.Descriptor providerDescriptor = indexDescriptor.providerDescriptor();
UTF8.putEncodedStringInto( providerDescriptor.getKey(), target );
UTF8.putEncodedStringInto( providerDescriptor.getVersion(), target );
switch ( indexDescriptor.type() )
{
case GENERAL:
target.put( GENERAL_INDEX );
break;
case UNIQUE:
target.put( UNIQUE_INDEX );
// The owning constraint can be null. See IndexRule.getOwningConstraint()
Long owningConstraint = indexDescriptor.getOwningConstraint();
target.putLong( owningConstraint == null ? NO_OWNING_CONSTRAINT_YET : owningConstraint );
break;
default:
throw new UnsupportedOperationException( format( "Got unknown index descriptor type '%s'.",
indexDescriptor.type() ) );
}
indexDescriptor.schema().processWith( new SchemaDescriptorSerializer( target ) );
UTF8.putEncodedStringInto( indexDescriptor.getName(), target );
return target.array();
}
/**
* Serialize the provided ConstraintRule onto the target buffer
* @param constraintRule the ConstraintRule to serialize
* @throws IllegalStateException if the ConstraintRule is of type unique, but the owned index has not been set
*/
public static byte[] serialize( ConstraintRule constraintRule )
{
ByteBuffer target = ByteBuffer.allocate( lengthOf( constraintRule ) );
target.putInt( LEGACY_LABEL_OR_REL_TYPE_ID );
target.put( CONSTRAINT_RULE );
ConstraintDescriptor constraintDescriptor = constraintRule.getConstraintDescriptor();
switch ( constraintDescriptor.type() )
{
case EXISTS:
target.put( EXISTS_CONSTRAINT );
break;
case UNIQUE:
target.put( UNIQUE_CONSTRAINT );
target.putLong( constraintRule.getOwnedIndex() );
break;
case UNIQUE_EXISTS:
target.put( UNIQUE_EXISTS_CONSTRAINT );
target.putLong( constraintRule.getOwnedIndex() );
break;
default:
throw new UnsupportedOperationException( format( "Got unknown index descriptor type '%s'.",
constraintDescriptor.type() ) );
}
constraintDescriptor.schema().processWith( new SchemaDescriptorSerializer( target ) );
UTF8.putEncodedStringInto( constraintRule.getName(), target );
return target.array();
}
/**
* Compute the byte size needed to serialize the provided IndexRule using serialize.
* @param indexDescriptor the StoreIndexDescriptor
* @return the byte size of StoreIndexDescriptor
*/
public static int lengthOf( StoreIndexDescriptor indexDescriptor )
{
int length = 4; // legacy label or relType id
length += 1; // schema rule type
IndexProvider.Descriptor providerDescriptor = indexDescriptor.providerDescriptor();
length += UTF8.computeRequiredByteBufferSize( providerDescriptor.getKey() );
length += UTF8.computeRequiredByteBufferSize( providerDescriptor.getVersion() );
length += 1; // index type
if ( indexDescriptor.type() == IndexDescriptor.Type.UNIQUE )
{
length += 8; // owning constraint id
}
length += indexDescriptor.schema().computeWith( schemaSizeComputer );
length += UTF8.computeRequiredByteBufferSize( indexDescriptor.getName() );
return length;
}
/**
* Compute the byte size needed to serialize the provided ConstraintRule using serialize.
* @param constraintRule the ConstraintRule
* @return the byte size of ConstraintRule
*/
public static int lengthOf( ConstraintRule constraintRule )
{
int length = 4; // legacy label or relType id
length += 1; // schema rule type
length += 1; // constraint type
ConstraintDescriptor constraintDescriptor = constraintRule.getConstraintDescriptor();
if ( constraintDescriptor.enforcesUniqueness() )
{
length += 8; // owned index id
}
length += constraintDescriptor.schema().computeWith( schemaSizeComputer );
length += UTF8.computeRequiredByteBufferSize( constraintRule.getName() );
return length;
}
// PRIVATE
// READ INDEX
private static StoreIndexDescriptor readIndexRule( long id, ByteBuffer source ) throws MalformedSchemaRuleException
{
IndexProvider.Descriptor indexProvider = readIndexProviderDescriptor( source );
LabelSchemaDescriptor schema;
byte indexRuleType = source.get();
String name;
switch ( indexRuleType )
{
case GENERAL_INDEX:
schema = readLabelSchema( source );
name = readRuleName( source );
return StoreIndexDescriptor.indexRule( id, IndexDescriptorFactory.forSchema( schema ), indexProvider, name );
case UNIQUE_INDEX:
long owningConstraint = source.getLong();
schema = readLabelSchema( source );
IndexDescriptor descriptor = IndexDescriptorFactory.uniqueForSchema( schema );
name = readRuleName( source );
return StoreIndexDescriptor.constraintIndexRule( id, descriptor, indexProvider,
owningConstraint == NO_OWNING_CONSTRAINT_YET ? null : owningConstraint, name );
default:
throw new MalformedSchemaRuleException( format( "Got unknown index rule type '%d'.", indexRuleType ) );
}
}
private static LabelSchemaDescriptor readLabelSchema( ByteBuffer source ) throws MalformedSchemaRuleException
{
SchemaDescriptor schemaDescriptor = readSchema( source );
if ( !(schemaDescriptor instanceof LabelSchemaDescriptor) )
{
throw new MalformedSchemaRuleException( "IndexRules must have LabelSchemaDescriptors, got " +
schemaDescriptor.getClass().getSimpleName() );
}
return (LabelSchemaDescriptor)schemaDescriptor;
}
private static IndexProvider.Descriptor readIndexProviderDescriptor( ByteBuffer source )
{
String providerKey = getDecodedStringFrom( source );
String providerVersion = getDecodedStringFrom( source );
return new IndexProvider.Descriptor( providerKey, providerVersion );
}
// READ CONSTRAINT
private static ConstraintRule readConstraintRule( long id, ByteBuffer source ) throws MalformedSchemaRuleException
{
SchemaDescriptor schema;
byte constraintRuleType = source.get();
String name;
switch ( constraintRuleType )
{
case EXISTS_CONSTRAINT:
schema = readSchema( source );
name = readRuleName( source );
return ConstraintRule.constraintRule( id, ConstraintDescriptorFactory.existsForSchema( schema ), name );
case UNIQUE_CONSTRAINT:
long ownedUniqueIndex = source.getLong();
schema = readSchema( source );
UniquenessConstraintDescriptor descriptor = ConstraintDescriptorFactory.uniqueForSchema( schema );
name = readRuleName( source );
return ConstraintRule.constraintRule( id, descriptor, ownedUniqueIndex, name );
case UNIQUE_EXISTS_CONSTRAINT:
long ownedNodeKeyIndex = source.getLong();
schema = readSchema( source );
NodeKeyConstraintDescriptor nodeKeyConstraintDescriptor = ConstraintDescriptorFactory.nodeKeyForSchema( schema );
name = readRuleName( source );
return ConstraintRule.constraintRule( id, nodeKeyConstraintDescriptor, ownedNodeKeyIndex, name );
default:
throw new MalformedSchemaRuleException( format( "Got unknown constraint rule type '%d'.", constraintRuleType ) );
}
}
private static String readRuleName( ByteBuffer source )
{
String ruleName = null;
if ( source.remaining() >= UTF8.MINIMUM_SERIALISED_LENGTH_BYTES )
{
ruleName = UTF8.getDecodedStringFrom( source );
if ( ruleName.isEmpty() )
{
ruleName = null;
}
}
return ruleName;
}
// READ HELP
private static SchemaDescriptor readSchema( ByteBuffer source ) throws MalformedSchemaRuleException
{
int[] propertyIds;
byte schemaDescriptorType = source.get();
switch ( schemaDescriptorType )
{
case SIMPLE_LABEL:
int labelId = source.getInt();
propertyIds = readPropertyIds( source );
return SchemaDescriptorFactory.forLabel( labelId, propertyIds );
case SIMPLE_REL_TYPE:
int relTypeId = source.getInt();
propertyIds = readPropertyIds( source );
return SchemaDescriptorFactory.forRelType( relTypeId, propertyIds );
default:
throw new MalformedSchemaRuleException( format( "Got unknown schema descriptor type '%d'.",
schemaDescriptorType ) );
}
}
private static int[] readPropertyIds( ByteBuffer source )
{
short numProperties = source.getShort();
int[] propertyIds = new int[numProperties];
for ( int i = 0; i < numProperties; i++ )
{
propertyIds[i] = source.getInt();
}
return propertyIds;
}
// WRITE
private static class SchemaDescriptorSerializer implements SchemaProcessor
{
private final ByteBuffer target;
SchemaDescriptorSerializer( ByteBuffer target )
{
this.target = target;
}
@Override
public void processSpecific( LabelSchemaDescriptor schema )
{
target.put( SIMPLE_LABEL );
target.putInt( schema.getLabelId() );
int[] propertyIds = schema.getPropertyIds();
target.putShort( (short)propertyIds.length );
for ( int propertyId : propertyIds )
{
target.putInt( propertyId );
}
}
@Override
public void processSpecific( RelationTypeSchemaDescriptor schema )
{
target.put( SIMPLE_REL_TYPE );
target.putInt( schema.getRelTypeId() );
int[] propertyIds = schema.getPropertyIds();
target.putShort( (short)propertyIds.length );
for ( int propertyId : propertyIds )
{
target.putInt( propertyId );
}
}
}
// LENGTH OF
private static SchemaComputer<Integer> schemaSizeComputer = new SchemaComputer<Integer>()
{
@Override
public Integer computeSpecific( LabelSchemaDescriptor schema )
{
return 1 // schema descriptor type
+ 4 // label id
+ 2 // property id count
+ 4 * schema.getPropertyIds().length; // the actual property ids
}
@Override
public Integer computeSpecific( RelationTypeSchemaDescriptor schema )
{
return 1 // schema descriptor type
+ 4 // rel type id
+ 2 // property id count
+ 4 * schema.getPropertyIds().length; // the actual property ids
}
};
}