in core/src/main/java/com/jetbrains/youtrackdb/internal/core/db/tool/DatabaseImport.java [497:753]
private void importSchema(boolean collectionsImported) throws IOException, ParseException {
if (!collectionsImported) {
removeDefaultCollections();
}
listener.onMessage("\nImporting database schema...");
jsonReader.readNext(JSONReader.BEGIN_OBJECT);
@SuppressWarnings("unused")
var schemaVersion =
jsonReader
.readNext(JSONReader.FIELD_ASSIGNMENT)
.checkContent("\"version\"")
.readNumber(JSONReader.ANY_NUMBER, true);
jsonReader.readNext(JSONReader.COMMA_SEPARATOR);
jsonReader.readNext(JSONReader.FIELD_ASSIGNMENT);
// This can be removed after the M1 expires
if (jsonReader.getValue().equals("\"globalProperties\"")) {
jsonReader.readNext(JSONReader.BEGIN_COLLECTION);
do {
jsonReader.readNext(JSONReader.BEGIN_OBJECT);
jsonReader.readNext(JSONReader.FIELD_ASSIGNMENT).checkContent("\"name\"");
jsonReader.readString(JSONReader.NEXT_IN_OBJECT);
jsonReader.readNext(JSONReader.FIELD_ASSIGNMENT).checkContent("\"global-id\"");
jsonReader.readString(JSONReader.NEXT_IN_OBJECT);
jsonReader.readNext(JSONReader.FIELD_ASSIGNMENT).checkContent("\"type\"");
jsonReader.readString(JSONReader.NEXT_IN_OBJECT);
jsonReader.readNext(JSONReader.NEXT_IN_ARRAY);
} while (jsonReader.lastChar() == ',');
jsonReader.readNext(JSONReader.COMMA_SEPARATOR);
jsonReader.readNext(JSONReader.FIELD_ASSIGNMENT);
}
if (jsonReader.getValue().equals("\"blob-collections\"") ||
jsonReader.getValue().equals("\"blob-clusters\"")) {
var blobCollectionIds = jsonReader.readString(JSONReader.END_COLLECTION, true).trim();
blobCollectionIds = blobCollectionIds.substring(1, blobCollectionIds.length() - 1);
if (!blobCollectionIds.isEmpty()) {
// READ BLOB COLLECTION IDS
for (var i :
StringSerializerHelper.split(
blobCollectionIds, StringSerializerHelper.RECORD_SEPARATOR)) {
var collection = Integer.parseInt(i.trim());
if (!ArrayUtils.contains(session.getBlobCollectionIds(), collection)) {
var name = session.getCollectionNameById(collection);
session.addBlobCollection(name);
}
}
}
jsonReader.readNext(JSONReader.COMMA_SEPARATOR);
jsonReader.readNext(JSONReader.FIELD_ASSIGNMENT);
}
jsonReader.checkContent("\"classes\"").readNext(JSONReader.BEGIN_COLLECTION);
long classImported = 0;
try {
// creating V and E classes ahead of time, because they have to exist
// before we start creating other vertex or edge classes.
// we tried to fix this by making the export tool write these classes first,
// but if the dump was created by an older version of the export tool,
// it won't work.
final var schema = session.getMetadata().getSchema();
final var vertexClass = schema.existsClass(Vertex.CLASS_NAME) ?
schema.getClass(Vertex.CLASS_NAME) : schema.createClass(Vertex.CLASS_NAME);
final var edgeClass = schema.existsClass(Edge.CLASS_NAME) ?
schema.getClass(Edge.CLASS_NAME) : schema.createClass(Edge.CLASS_NAME);
do {
jsonReader.readNext(JSONReader.BEGIN_OBJECT);
var className =
jsonReader
.readNext(JSONReader.FIELD_ASSIGNMENT)
.checkContent("\"name\"")
.readString(JSONReader.COMMA_SEPARATOR);
final var collectionIdsTag =
exporterVersion >= 14 ? "\"collection-ids\"" : "\"cluster-ids\"";
final var collectionIdsStr = jsonReader
.readNext(JSONReader.FIELD_ASSIGNMENT)
.checkContent(collectionIdsTag)
.readString(JSONReader.END_COLLECTION, true)
.trim();
final var originalCollectionIds =
StringSerializerHelper.splitIntArray(
collectionIdsStr.substring(1, collectionIdsStr.length() - 1));
// it's important to use previously created collections here because later the indexes
// are created on collections (not on classes).
final var newCollectionIds =
Arrays.stream(originalCollectionIds)
.map(collectionToCollectionMapping::get)
.filter(cid -> cid != COLLECTION_NOT_FOUND_VALUE)
.toArray();
jsonReader.readNext(JSONReader.NEXT_IN_OBJECT);
if (className.contains(".")) {
// MIGRATE OLD NAME WITH . TO _
final var newClassName = className.replace('.', '_');
listener.onMessage(
"\nWARNING: class '" + className + "' has been renamed in '" + newClassName + "'\n");
className = newClassName;
}
Boolean strictMode = null;
Boolean isAbstract = null;
var isVertex = false;
var isEdge = false;
Map<String, String> customFields = null;
List<Map<String, Object>> propertiesRaw = null;
String value;
while (jsonReader.lastChar() == ',') {
jsonReader.readNext(JSONReader.FIELD_ASSIGNMENT);
value = jsonReader.getValue();
switch (value) {
case "\"strictMode\"" -> strictMode = jsonReader.readBoolean(JSONReader.NEXT_IN_OBJECT);
case "\"abstract\"" -> isAbstract = jsonReader.readBoolean(JSONReader.NEXT_IN_OBJECT);
case "\"super-class\"" -> {
// @compatibility <2.1 SINGLE CLASS ONLY
final var classSuper = jsonReader.readString(JSONReader.NEXT_IN_OBJECT);
if (SchemaClass.VERTEX_CLASS_NAME.equals(classSuper)) {
isVertex = true;
} else if (SchemaClass.EDGE_CLASS_NAME.equals(classSuper)) {
isEdge = true;
} else {
final List<String> superClassNames = new ArrayList<>();
superClassNames.add(classSuper);
superClasses.put(className, superClassNames);
}
}
case "\"super-classes\"" -> {
// MULTIPLE CLASSES
jsonReader.readNext(JSONReader.BEGIN_COLLECTION);
final List<String> superClassNames = new ArrayList<>();
while (jsonReader.lastChar() != ']') {
jsonReader.readNext(JSONReader.NEXT_IN_ARRAY);
final var clsName =
IOUtils.getStringContent(StringUtils.trim(jsonReader.getValue()));
if (SchemaClass.VERTEX_CLASS_NAME.equals(clsName)) {
isVertex = true;
} else if (SchemaClass.EDGE_CLASS_NAME.equals(clsName)) {
isEdge = true;
} else {
superClassNames.add(clsName);
}
}
if (!superClassNames.isEmpty()) {
superClasses.put(className, superClassNames);
}
jsonReader.readNext(JSONReader.NEXT_IN_OBJECT);
}
case "\"properties\"" -> {
propertiesRaw = new ArrayList<>();
// GET PROPERTIES
jsonReader.readNext(JSONReader.BEGIN_COLLECTION);
while (jsonReader.lastChar() != ']') {
final var pRaw = jsonReader.readNext(JSONReader.NEXT_IN_ARRAY).getValue();
if (StringUtils.isNotBlank(pRaw)) {
final var pMap = jsonSerializer.mapFromJson(pRaw);
propertiesRaw.add(pMap);
}
}
jsonReader.readNext(JSONReader.NEXT_IN_OBJECT);
}
case "\"cluster-selection\"" ->
// ignoring old property
jsonReader.readNext(JSONReader.NEXT_IN_OBJECT);
case "\"customFields\"" -> {
customFields = importCustomFields();
}
}
}
if (isVertex && isEdge) {
throw new DatabaseImportException(
"Class '" + className + "' cannot be both vertex and edge.");
}
var cls = schema.getClass(className);
if (cls != null) {
if (isVertex && !cls.isVertexType()) {
throw new DatabaseImportException("Class '" + className
+ "' exists but is not a vertex class. It can't be made a vertex class.");
} else if (isEdge && !cls.isEdgeType()) {
throw new DatabaseImportException("Class '" + className
+ "' exists but is not an edge class. It can't be made an edge class."
);
}
} else {
if (collectionsImported) {
// other superclasses will be added later.
final var superClassesToAdd =
isVertex ? new SchemaClass[]{vertexClass} :
isEdge ? new SchemaClass[]{edgeClass} :
new SchemaClass[]{};
cls = schema.createClass(className, newCollectionIds, superClassesToAdd);
} else if (className.equalsIgnoreCase("ORestricted")) {
cls = schema.createAbstractClass(className);
} else {
cls = schema.createClass(className);
}
}
if (strictMode != null) {
cls.setStrictMode(strictMode);
}
if (isAbstract != null) {
cls.setAbstract(isAbstract);
}
if (propertiesRaw != null) {
for (var propRaw : propertiesRaw) {
importProperty((SchemaClassInternal) cls, propRaw);
}
}
if (customFields != null) {
for (var cf : customFields.entrySet()) {
cls.setCustom(cf.getKey(), cf.getValue());
}
}
classImported++;
jsonReader.readNext(JSONReader.NEXT_IN_ARRAY);
} while (jsonReader.lastChar() == ',');
this.rebuildCompleteClassInheritance();
this.setLinkedClasses();
if (exporterVersion < 11) {
var role = session.getMetadata().getSchema().getClass(Role.CLASS_NAME);
role.dropProperty("rules");
}
listener.onMessage("OK (" + classImported + " classes)");
jsonReader.readNext(JSONReader.END_OBJECT);
jsonReader.readNext(JSONReader.COMMA_SEPARATOR);
} catch (final Exception e) {
LogManager.instance().error(this, "Error on importing schema", e);
listener.onMessage("ERROR (" + classImported + " entries): " + e);
}
}