@Override public Void caseSimpleSchemaField(SimpleSchemaField field) { final byte[] value = ObjectType.this.checkSimpleField(info, id, field, fieldPrefix, i); simpleFieldValues.put(field.getStorageId(), value); if (value != null) indexedSimpleFieldsWithDefaultValues.remove(field); return null; } @Override
private void verifySimpleIndexEntry(JsckInfo info, ObjId id, SimpleSchemaField subField, ComplexSchemaField field, byte[] value, byte[] suffix) { this.verifySimpleIndexEntry(info, id, subField.getStorageId(), "sub-" + subField + " of " + field + " index", value, suffix); }
private void verifySimpleIndexEntry(JsckInfo info, ObjId id, SimpleSchemaField field, byte[] value) { this.verifySimpleIndexEntry(info, id, field.getStorageId(), "" + field + " index", value, ByteUtil.EMPTY); }
@Override public Void caseSimpleSchemaField(SimpleSchemaField field) { final FieldType<?> fieldType = info.getConfig().getFieldTypeRegistry().getFieldType(field.getType()); ObjectType.this.simpleFieldTypes.put(field.getStorageId(), fieldType); return null; } @Override
SimpleIndex(JsckInfo info, int schemaVersion, SimpleSchemaField field) { super(info, field.getStorageId()); assert field.isIndexed(); this.type = this.info.findFieldType(schemaVersion, field).genericizeForIndex(); }
private void doValidate() { // Validate object types and verify object type names are unique final TreeMap<String, SchemaObjectType> schemaObjectTypesByName = new TreeMap<>(); for (SchemaObjectType schemaObjectType : this.schemaObjectTypes.values()) { schemaObjectType.validate(); final String schemaObjectTypeName = schemaObjectType.getName(); final SchemaObjectType otherSchemaObjectType = schemaObjectTypesByName.put(schemaObjectTypeName, schemaObjectType); if (otherSchemaObjectType != null) throw new InvalidSchemaException("duplicate object name `" + schemaObjectTypeName + "'"); } // Collect all field storage ID's final TreeMap<Integer, AbstractSchemaItem> globalItemsByStorageId = new TreeMap<>(); for (SchemaObjectType schemaObjectType : this.schemaObjectTypes.values()) { for (SchemaField field : schemaObjectType.getSchemaFields().values()) { globalItemsByStorageId.put(field.getStorageId(), field); if (field instanceof ComplexSchemaField) { final ComplexSchemaField complexField = (ComplexSchemaField)field; for (SimpleSchemaField subField : complexField.getSubFields().values()) globalItemsByStorageId.put(subField.getStorageId(), subField); } } } // Verify object type, field, and index storage ID's are non-overlapping for (SchemaObjectType schemaObjectType : this.schemaObjectTypes.values()) { SchemaModel.verifyUniqueStorageId(globalItemsByStorageId, schemaObjectType); for (SchemaCompositeIndex index : schemaObjectType.getSchemaCompositeIndexes().values()) SchemaModel.verifyUniqueStorageId(globalItemsByStorageId, index); } }
private <T> SimpleField<T> buildSimpleField(SimpleSchemaField field, String fieldName, FieldType<T> fieldType) { assert field.getEncodingSignature() == fieldType.getEncodingSignature(); return new SimpleField<>(fieldName, field.getStorageId(), this.schema, fieldType, field.isIndexed()); }
private boolean validateSimpleFieldValue(JsckInfo info, ObjId id, SimpleSchemaField field, KVPair pair, ByteReader reader) { // Verify field encoding final FieldType<?> fieldType = this.simpleFieldTypes.get(field.getStorageId()); assert fieldType != null; try { // Decode value final Object value = fieldType.read(reader); if (reader.remain() > 0) throw new IllegalArgumentException("trailing garbage " + Jsck.ds(reader, reader.getOffset())); // For reference fields, check for illegal dangling references if (value != null && field instanceof ReferenceSchemaField) { final ReferenceSchemaField referenceField = (ReferenceSchemaField)field; if (!referenceField.isAllowDeleted()) { assert fieldType instanceof ReferenceFieldType; final ObjId target = (ObjId)value; if (info.getKVStore().get(target.getBytes()) == null) throw new IllegalArgumentException("invalid reference to deleted object " + target); } } } catch (IllegalArgumentException e) { info.handle(new InvalidValue(pair).setDetail(id, field, e.getMessage())); return false; } return true; }
final FieldType<?> fieldType = this.simpleFieldTypes.get(field.getStorageId()); final byte[] defaultValue = fieldType.getDefaultValue(); this.verifySimpleIndexEntry(info, id, field, defaultValue);
private byte[] checkSimpleField(JsckInfo info, ObjId id, SimpleSchemaField field, byte[] prefix, PeekingIterator<KVPair> i) { // Get field type final FieldType<?> fieldType = this.simpleFieldTypes.get(field.getStorageId()); assert fieldType != null; // Get field key/value pair final KVPair pair = i.next(); assert pair != null; assert ByteUtil.isPrefixOf(prefix, pair.getKey()); // Check for trailing garbage in key if (pair.getKey().length > prefix.length) { info.handle(new InvalidKey(pair).setDetail(id, field, "trailing garbage " + Jsck.ds(new ByteReader(pair.getKey(), prefix.length)))); return null; } // Decode value byte[] value = pair.getValue(); final ByteReader reader = new ByteReader(pair.getValue()); if (!this.validateSimpleFieldValue(info, id, field, pair, reader)) value = null; // We should not see default values in simple fields that are not sub-fields of complex fields if (value != null && ByteUtil.compare(value, fieldType.getDefaultValue()) == 0) { info.handle(new InvalidValue(pair).setDetail("default value; should not be present")); value = null; } // Verify index entry if (field.isIndexed()) this.verifySimpleIndexEntry(info, id, field, value != null ? value : fieldType.getDefaultValue()); // Done return value; }