Last active
September 22, 2016 06:28
-
-
Save smolinari/609335f498c456bd97c3ff86ad6136db to your computer and use it in GitHub Desktop.
Example ODB Cluster to Shard Code Change
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* | |
* * Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com) | |
* * | |
* * Licensed under the Apache License, Version 2.0 (the "License"); | |
* * you may not use this file except in compliance with the License. | |
* * You may obtain a copy of the License at | |
* * | |
* * http://www.apache.org/licenses/LICENSE-2.0 | |
* * | |
* * Unless required by applicable law or agreed to in writing, software | |
* * distributed under the License is distributed on an "AS IS" BASIS, | |
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* * See the License for the specific language governing permissions and | |
* * limitations under the License. | |
* * | |
* * For more information: http://www.orientechnologies.com | |
* | |
*/ | |
package com.orientechnologies.orient.core.metadata.schema; | |
import com.orientechnologies.common.exception.OException; | |
import com.orientechnologies.common.listener.OProgressListener; | |
import com.orientechnologies.common.log.OLogManager; | |
import com.orientechnologies.common.util.OArrays; | |
import com.orientechnologies.common.util.OCommonConst; | |
import com.orientechnologies.orient.core.annotation.OBeforeSerialization; | |
import com.orientechnologies.orient.core.command.OCommandResultListener; | |
import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal; | |
import com.orientechnologies.orient.core.db.ODatabaseInternal; | |
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal; | |
import com.orientechnologies.orient.core.db.OScenarioThreadLocal; | |
import com.orientechnologies.orient.core.db.document.ODatabaseDocument; | |
import com.orientechnologies.orient.core.db.record.OIdentifiable; | |
import com.orientechnologies.orient.core.db.record.ORecordElement; | |
import com.orientechnologies.orient.core.exception.ODatabaseException; | |
import com.orientechnologies.orient.core.exception.OSchemaException; | |
import com.orientechnologies.orient.core.exception.OSecurityAccessException; | |
import com.orientechnologies.orient.core.exception.OSecurityException; | |
import com.orientechnologies.orient.core.id.ORecordId; | |
import com.orientechnologies.orient.core.index.*; | |
import com.orientechnologies.orient.core.metadata.schema.shardselection.OShardSelectionStrategy; | |
import com.orientechnologies.orient.core.metadata.security.ORole; | |
import com.orientechnologies.orient.core.metadata.security.ORule; | |
import com.orientechnologies.orient.core.metadata.security.OSecurityShared; | |
import com.orientechnologies.orient.core.metadata.security.OSecurityUser; | |
import com.orientechnologies.orient.core.record.ORecord; | |
import com.orientechnologies.orient.core.record.ORecordInternal; | |
import com.orientechnologies.orient.core.record.impl.ODocument; | |
import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSerializerFactory; | |
import com.orientechnologies.orient.core.serialization.serializer.record.string.ORecordSerializerSchemaAware2CSV; | |
import com.orientechnologies.orient.core.sharding.auto.OAutoShardingShardSelectionStrategy; | |
import com.orientechnologies.orient.core.sql.OCommandSQL; | |
import com.orientechnologies.orient.core.sql.query.OSQLAsynchQuery; | |
import com.orientechnologies.orient.core.storage.*; | |
import com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage; | |
import com.orientechnologies.orient.core.type.ODocumentWrapper; | |
import com.orientechnologies.orient.core.type.ODocumentWrapperNoClass; | |
import java.io.IOException; | |
import java.io.UnsupportedEncodingException; | |
import java.util.*; | |
import java.util.concurrent.Callable; | |
/** | |
* Schema Class implementation. | |
* | |
* @author Luca Garulli (l.garulli--at--orientechnologies.com) | |
*/ | |
@SuppressWarnings("unchecked") public class OClassImpl extends ODocumentWrapperNoClass implements OClass { | |
private static final long serialVersionUID = 1L; | |
private static final int NOT_EXISTENT_SHARD_ID = -1; | |
final OSchemaShared owner; | |
private final Map<String, OProperty> properties = new HashMap<String, OProperty>(); | |
private int defaultShardId = NOT_EXISTENT_SHARD_ID; | |
private String name; | |
private String description; | |
private int[] shardIds; | |
private List<OClassImpl> superClasses = new ArrayList<OClassImpl>(); | |
private int[] polymorphicShardIds; | |
private List<OClass> subclasses; | |
private float overSize = 0f; | |
private String shortName; | |
private boolean strictMode = false; // @SINCE v1.0rc8 | |
private boolean abstractClass = false; // @SINCE v1.2.0 | |
private Map<String, String> customFields; | |
private volatile OShardSelectionStrategy shardSelection; // @SINCE 1.7 | |
private volatile int hashCode; | |
private static Set<String> reserved = new HashSet<String>(); | |
static { | |
// reserved.add("select"); | |
reserved.add("traverse"); | |
reserved.add("insert"); | |
reserved.add("update"); | |
reserved.add("delete"); | |
reserved.add("from"); | |
reserved.add("where"); | |
reserved.add("skip"); | |
reserved.add("limit"); | |
reserved.add("timeout"); | |
} | |
/** | |
* Constructor used in unmarshalling. | |
*/ | |
protected OClassImpl(final OSchemaShared iOwner, final String iName) { | |
this(iOwner, new ODocument().setTrackingChanges(false), iName); | |
} | |
protected OClassImpl(final OSchemaShared iOwner, final String iName, final int[] iShardIds) { | |
this(iOwner, iName); | |
setShardIds(iShardIds); | |
defaultShardId = iShardIds[0]; | |
if (defaultShardId == NOT_EXISTENT_SHARD_ID) | |
abstractClass = true; | |
if (abstractClass) | |
setPolymorphicShardIds(OCommonConst.EMPTY_INT_ARRAY); | |
else | |
setPolymorphicShardIds(iShardIds); | |
shardSelection = owner.getShardSelectionFactory().newInstanceOfDefaultClass(); | |
} | |
/** | |
* Constructor used in unmarshalling. | |
*/ | |
protected OClassImpl(final OSchemaShared iOwner, final ODocument iDocument, final String iName) { | |
name = iName; | |
document = iDocument; | |
owner = iOwner; | |
} | |
public static int[] readableShards(final ODatabaseDocument iDatabase, final int[] iShardIds) { | |
List<Integer> listOfReadableIds = new ArrayList<Integer>(); | |
boolean all = true; | |
for (int shardId : iShardIds) { | |
try { | |
final String shardName = iDatabase.getShardNameById(shardId); | |
iDatabase.checkSecurity(ORule.ResourceGeneric.SHARD, ORole.PERMISSION_READ, shardName); | |
listOfReadableIds.add(shardId); | |
} catch (OSecurityAccessException securityException) { | |
all = false; | |
// if the shard is inaccessible it's simply not processed in the list.add | |
} | |
} | |
if (all) | |
// JUST RETURN INPUT ARRAY (FASTER) | |
return iShardIds; | |
final int[] readableShardIds = new int[listOfReadableIds.size()]; | |
int index = 0; | |
for (int shardId : listOfReadableIds) { | |
readableShardIds[index++] = shardId; | |
} | |
return readableShardIds; | |
} | |
@Override public OShardSelectionStrategy getShardSelection() { | |
acquireSchemaReadLock(); | |
try { | |
return shardSelection; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Override public OClass setShardSelection(final OShardSelectionStrategy shardSelection) { | |
return setShardSelection(shardSelection.getName()); | |
} | |
@Override public OClass setShardSelection(final String value) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` shardselection '%s'", name, value); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` shardselection '%s'", name, value); | |
OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
setShardSelectionInternal(value); | |
} else | |
setShardSelectionInternal(value); | |
return this; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
@Override public <RET extends ODocumentWrapper> RET reload() { | |
return (RET) owner.reload(); | |
} | |
public String getCustom(final String iName) { | |
acquireSchemaReadLock(); | |
try { | |
if (customFields == null) | |
return null; | |
return customFields.get(iName); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public OClassImpl setCustom(final String name, final String value) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` custom %s=%s", getName(), name, value); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` custom %s=%s", getName(), name, value); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
setCustomInternal(name, value); | |
} else | |
setCustomInternal(name, value); | |
return this; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
public Map<String, String> getCustomInternal() { | |
acquireSchemaReadLock(); | |
try { | |
if (customFields != null) | |
return Collections.unmodifiableMap(customFields); | |
return null; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public void removeCustom(final String name) { | |
setCustom(name, null); | |
} | |
public void clearCustom() { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` custom clear", getName()); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` custom clear", getName()); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
clearCustomInternal(); | |
} else | |
clearCustomInternal(); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
public Set<String> getCustomKeys() { | |
acquireSchemaReadLock(); | |
try { | |
if (customFields != null) | |
return Collections.unmodifiableSet(customFields.keySet()); | |
return new HashSet<String>(); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Override public boolean hasShardId(final int shardId) { | |
return Arrays.binarySearch(shardIds, shardId) >= 0; | |
} | |
@Override public boolean hasPolymorphicShardId(final int shardId) { | |
return Arrays.binarySearch(polymorphicShardIds, shardId) >= 0; | |
} | |
@Override | |
@Deprecated | |
public OClass getSuperClass() { | |
acquireSchemaReadLock(); | |
try { | |
return superClasses.isEmpty() ? null : superClasses.get(0); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Override | |
@Deprecated | |
public OClass setSuperClass(OClass iSuperClass) { | |
setSuperClasses(iSuperClass != null ? Arrays.asList(iSuperClass) : Collections.EMPTY_LIST); | |
return this; | |
} | |
public String getName() { | |
acquireSchemaReadLock(); | |
try { | |
return name; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Override public List<OClass> getSuperClasses() { | |
acquireSchemaReadLock(); | |
try { | |
return Collections.unmodifiableList((List<? extends OClass>) superClasses); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Override public boolean hasSuperClasses() { | |
acquireSchemaReadLock(); | |
try { | |
return !superClasses.isEmpty(); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Override public List<String> getSuperClassesNames() { | |
acquireSchemaReadLock(); | |
try { | |
List<String> superClassesNames = new ArrayList<String>(superClasses.size()); | |
for (OClassImpl superClass : superClasses) { | |
superClassesNames.add(superClass.getName()); | |
} | |
return superClassesNames; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public OClass setSuperClassesByNames(List<String> classNames) { | |
if (classNames == null) | |
classNames = Collections.EMPTY_LIST; | |
final List<OClass> classes = new ArrayList<OClass>(classNames.size()); | |
final OSchema schema = getDatabase().getMetadata().getSchema(); | |
for (String className : classNames) { | |
classes.add(schema.getClass(decodeClassName(className))); | |
} | |
return setSuperClasses(classes); | |
} | |
@Override public OClass setSuperClasses(final List<? extends OClass> classes) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
if (classes != null) { | |
List<OClass> toCheck = new ArrayList<OClass>(classes); | |
toCheck.add(this); | |
checkParametersConflict(toCheck); | |
} | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
final StringBuilder sb = new StringBuilder(); | |
if (classes != null && classes.size() > 0) { | |
for (OClass superClass : classes) { | |
sb.append('`').append(superClass.getName()).append("`,"); | |
} | |
sb.deleteCharAt(sb.length() - 1); | |
} else | |
sb.append("null"); | |
final String cmd = String.format("alter class `%s` superclasses %s", name, sb); | |
if (storage instanceof OStorageProxy) { | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
setSuperClassesInternal(classes); | |
} else | |
setSuperClassesInternal(classes); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
void setSuperClassesInternal(final List<? extends OClass> classes) { | |
acquireSchemaWriteLock(); | |
try { | |
List<OClassImpl> newSuperClasses = new ArrayList<OClassImpl>(); | |
OClassImpl cls; | |
for (OClass superClass : classes) { | |
if (superClass instanceof OClassAbstractDelegate) | |
cls = (OClassImpl) ((OClassAbstractDelegate) superClass).delegate; | |
else | |
cls = (OClassImpl) superClass; | |
if (newSuperClasses.contains(cls)) { | |
throw new OSchemaException("Duplicated superclass '" + cls.getName() + "'"); | |
} | |
newSuperClasses.add(cls); | |
} | |
List<OClassImpl> toAddList = new ArrayList<OClassImpl>(newSuperClasses); | |
toAddList.removeAll(superClasses); | |
List<OClassImpl> toRemoveList = new ArrayList<OClassImpl>(superClasses); | |
toRemoveList.removeAll(newSuperClasses); | |
for (OClassImpl toRemove : toRemoveList) { | |
toRemove.removeBaseClassInternal(this); | |
} | |
for (OClassImpl addTo : toAddList) { | |
addTo.addBaseClass(this); | |
} | |
superClasses.clear(); | |
superClasses.addAll(newSuperClasses); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
@Override public OClass addSuperClass(final OClass superClass) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
checkParametersConflict(superClass); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String | |
.format("alter class `%s` superclass +`%s`", name, superClass != null ? superClass.getName() : null); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String | |
.format("alter class `%s` superclass +`%s`", name, superClass != null ? superClass.getName() : null); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
addSuperClassInternal(superClass); | |
} else | |
addSuperClassInternal(superClass); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
void addSuperClassInternal(final OClass superClass) { | |
acquireSchemaWriteLock(); | |
try { | |
final OClassImpl cls; | |
if (superClass instanceof OClassAbstractDelegate) | |
cls = (OClassImpl) ((OClassAbstractDelegate) superClass).delegate; | |
else | |
cls = (OClassImpl) superClass; | |
if (cls != null) { | |
// CHECK THE USER HAS UPDATE PRIVILEGE AGAINST EXTENDING CLASS | |
final OSecurityUser user = getDatabase().getUser(); | |
if (user != null) | |
user.allow(ORule.ResourceGeneric.CLASS, cls.getName(), ORole.PERMISSION_UPDATE); | |
if (superClasses.contains(superClass)) { | |
throw new OSchemaException( | |
"Class: '" + this.getName() + "' already has the class '" + superClass.getName() + "' as superclass"); | |
} | |
cls.addBaseClass(this); | |
superClasses.add(cls); | |
} | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
@Override public OClass removeSuperClass(OClass superClass) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String | |
.format("alter class `%s` superclass -`%s`", name, superClass != null ? superClass.getName() : null); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String | |
.format("alter class `%s` superclass -`%s`", name, superClass != null ? superClass.getName() : null); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
removeSuperClassInternal(superClass); | |
} else | |
removeSuperClassInternal(superClass); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
void removeSuperClassInternal(final OClass superClass) { | |
acquireSchemaWriteLock(); | |
try { | |
final OClassImpl cls; | |
if (superClass instanceof OClassAbstractDelegate) | |
cls = (OClassImpl) ((OClassAbstractDelegate) superClass).delegate; | |
else | |
cls = (OClassImpl) superClass; | |
if (superClasses.contains(cls)) { | |
if (cls != null) | |
cls.removeBaseClassInternal(this); | |
superClasses.remove(superClass); | |
} | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
public OClass setName(final String name) { | |
if (getName().equals(name)) | |
return this; | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
final Character wrongCharacter = OSchemaShared.checkClassNameIfValid(name); | |
OClass oClass = getDatabase().getMetadata().getSchema().getClass(name); | |
if (oClass != null) { | |
String error = String.format("Cannot rename class %s to %s. A Class with name %s exists", this.name, name, name); | |
throw new OSchemaException(error); | |
} | |
if (wrongCharacter != null) | |
throw new OSchemaException( | |
"Invalid class name found. Character '" + wrongCharacter + "' cannot be used in class name '" + name + "'"); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` name `%s`", this.name, name); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` name `%s`", this.name, name); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
setNameInternal(name); | |
} else | |
setNameInternal(name); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
public long getSize() { | |
acquireSchemaReadLock(); | |
try { | |
long size = 0; | |
for (int shardId : shardIds) | |
size += getDatabase().getShardRecordSizeById(shardId); | |
return size; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public String getShortName() { | |
acquireSchemaReadLock(); | |
try { | |
return shortName; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public OClass setShortName(String shortName) { | |
if (shortName != null) { | |
shortName = shortName.trim(); | |
if (shortName.isEmpty()) | |
shortName = null; | |
} | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` shortname %s", name, shortName); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` shortname %s", name, shortName); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
setShortNameInternal(shortName); | |
} else | |
setShortNameInternal(shortName); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
public String getDescription() { | |
acquireSchemaReadLock(); | |
try { | |
return description; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public OClass setDescription(String iDescription) { | |
if (iDescription != null) { | |
iDescription = iDescription.trim(); | |
if (iDescription.isEmpty()) | |
iDescription = null; | |
} | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` description %s", name, shortName); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` description %s", name, shortName); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
setDescriptionInternal(iDescription); | |
} else | |
setDescriptionInternal(iDescription); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
public String getStreamableName() { | |
acquireSchemaReadLock(); | |
try { | |
return shortName != null ? shortName : name; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public Collection<OProperty> declaredProperties() { | |
acquireSchemaReadLock(); | |
try { | |
return Collections.unmodifiableCollection(properties.values()); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public Map<String, OProperty> propertiesMap() { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_READ); | |
acquireSchemaReadLock(); | |
try { | |
final Map<String, OProperty> props = new HashMap<String, OProperty>(20); | |
propertiesMap(props, true); | |
return props; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
private void propertiesMap(Map<String, OProperty> propertiesMap, boolean keepCase) { | |
for (OProperty p : properties.values()) { | |
String propName = p.getName(); | |
if (!keepCase) | |
propName = propName.toLowerCase(); | |
if (!propertiesMap.containsKey(propName)) | |
propertiesMap.put(propName, p); | |
} | |
for (OClassImpl superClass : superClasses) { | |
superClass.propertiesMap(propertiesMap, keepCase); | |
} | |
} | |
public Collection<OProperty> properties() { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_READ); | |
acquireSchemaReadLock(); | |
try { | |
final Collection<OProperty> props = new ArrayList<OProperty>(); | |
properties(props); | |
return props; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
private void properties(Collection<OProperty> properties) { | |
properties.addAll(this.properties.values()); | |
for (OClassImpl superClass : superClasses) { | |
superClass.properties(properties); | |
} | |
} | |
public void getIndexedProperties(Collection<OProperty> indexedProperties) { | |
for (OProperty p : properties.values()) | |
if (areIndexed(p.getName())) | |
indexedProperties.add(p); | |
for (OClassImpl superClass : superClasses) { | |
superClass.getIndexedProperties(indexedProperties); | |
} | |
} | |
@Override public Collection<OProperty> getIndexedProperties() { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_READ); | |
acquireSchemaReadLock(); | |
try { | |
Collection<OProperty> indexedProps = new HashSet<OProperty>(); | |
getIndexedProperties(indexedProps); | |
return indexedProps; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public OProperty getProperty(String propertyName) { | |
acquireSchemaReadLock(); | |
try { | |
propertyName = propertyName.toLowerCase(); | |
OProperty p = properties.get(propertyName); | |
if (p != null) | |
return p; | |
for (int i = 0; i < superClasses.size() && p == null; i++) { | |
p = superClasses.get(i).getProperty(propertyName); | |
} | |
return p; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public OProperty createProperty(final String iPropertyName, final OType iType) { | |
return addProperty(iPropertyName, iType, null, null, false); | |
} | |
public OProperty createProperty(final String iPropertyName, final OType iType, final OClass iLinkedClass) { | |
if (iLinkedClass == null) | |
throw new OSchemaException("Missing linked class"); | |
return addProperty(iPropertyName, iType, null, iLinkedClass, false); | |
} | |
public OProperty createProperty(final String iPropertyName, final OType iType, final OClass iLinkedClass, final boolean unsafe) { | |
if (iLinkedClass == null) | |
throw new OSchemaException("Missing linked class"); | |
return addProperty(iPropertyName, iType, null, iLinkedClass, unsafe); | |
} | |
public OProperty createProperty(final String iPropertyName, final OType iType, final OType iLinkedType) { | |
return addProperty(iPropertyName, iType, iLinkedType, null, false); | |
} | |
public OProperty createProperty(final String iPropertyName, final OType iType, final OType iLinkedType, final boolean unsafe) { | |
return addProperty(iPropertyName, iType, iLinkedType, null, unsafe); | |
} | |
@Override public boolean existsProperty(String propertyName) { | |
acquireSchemaReadLock(); | |
try { | |
propertyName = propertyName.toLowerCase(); | |
boolean result = properties.containsKey(propertyName); | |
if (result) | |
return true; | |
for (OClassImpl superClass : superClasses) { | |
result = superClass.existsProperty(propertyName); | |
if (result) | |
return true; | |
} | |
return false; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public void dropProperty(final String propertyName) { | |
if (getDatabase().getTransaction().isActive()) | |
throw new IllegalStateException("Cannot drop a property inside a transaction"); | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_DELETE); | |
final String lowerName = propertyName.toLowerCase(); | |
acquireSchemaWriteLock(); | |
try { | |
if (!properties.containsKey(lowerName)) | |
throw new OSchemaException("Property '" + propertyName + "' not found in class " + name + "'"); | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
database.command(new OCommandSQL("drop property " + name + '.' + propertyName)).execute(); | |
} else if (isDistributedCommand()) { | |
final OCommandSQL commandSQL = new OCommandSQL("drop property " + name + '.' + propertyName); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
OScenarioThreadLocal.executeAsDistributed(new Callable<OProperty>() { | |
@Override public OProperty call() throws Exception { | |
dropPropertyInternal(propertyName); | |
return null; | |
} | |
}); | |
} else | |
OScenarioThreadLocal.executeAsDistributed(new Callable<OProperty>() { | |
@Override public OProperty call() throws Exception { | |
dropPropertyInternal(propertyName); | |
return null; | |
} | |
}); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
@Override public void fromStream() { | |
subclasses = null; | |
superClasses.clear(); | |
name = document.field("name"); | |
if (document.containsField("shortName")) | |
shortName = document.field("shortName"); | |
else | |
shortName = null; | |
if (document.containsField("description")) | |
description = document.field("description"); | |
else | |
description = null; | |
defaultShardId = document.field("defaultShardId"); | |
if (document.containsField("strictMode")) | |
strictMode = document.field("strictMode"); | |
else | |
strictMode = false; | |
if (document.containsField("abstract")) | |
abstractClass = document.field("abstract"); | |
else | |
abstractClass = false; | |
if (document.field("overSize") != null) | |
overSize = document.field("overSize"); | |
else | |
overSize = 0f; | |
final Object cc = document.field("shardIds"); | |
if (cc instanceof Collection<?>) { | |
final Collection<Integer> coll = document.field("shardIds"); | |
shardIds = new int[coll.size()]; | |
int i = 0; | |
for (final Integer item : coll) | |
shardIds[i++] = item; | |
} else | |
shardIds = (int[]) cc; | |
Arrays.sort(shardIds); | |
if (shardIds.length == 1 && shardIds[0] == -1) | |
setPolymorphicShardIds(OCommonConst.EMPTY_INT_ARRAY); | |
else | |
setPolymorphicShardIds(shardIds); | |
// READ PROPERTIES | |
OPropertyImpl prop; | |
final Map<String, OProperty> newProperties = new HashMap<String, OProperty>(); | |
final Collection<ODocument> storedProperties = document.field("properties"); | |
if (storedProperties != null) | |
for (OIdentifiable id : storedProperties) { | |
ODocument p = id.getRecord(); | |
prop = new OPropertyImpl(this, p); | |
prop.fromStream(); | |
if (properties.containsKey(prop.getName())) { | |
prop = (OPropertyImpl) properties.get(prop.getName().toLowerCase()); | |
prop.fromStream(p); | |
} | |
newProperties.put(prop.getName().toLowerCase(), prop); | |
} | |
properties.clear(); | |
properties.putAll(newProperties); | |
customFields = document.field("customFields", OType.EMBEDDEDMAP); | |
shardSelection = owner.getShardSelectionFactory().getStrategy((String) document.field("shardSelection")); | |
} | |
@Override @OBeforeSerialization public ODocument toStream() { | |
document.setInternalStatus(ORecordElement.STATUS.UNMARSHALLING); | |
try { | |
document.field("name", name); | |
document.field("shortName", shortName); | |
document.field("description", description); | |
document.field("defaultShardId", defaultShardId); | |
document.field("shardIds", shardIds); | |
document.field("shardSelection", shardSelection.getName()); | |
document.field("overSize", overSize); | |
document.field("strictMode", strictMode); | |
document.field("abstract", abstractClass); | |
final Set<ODocument> props = new LinkedHashSet<ODocument>(); | |
for (final OProperty p : properties.values()) { | |
props.add(((OPropertyImpl) p).toStream()); | |
} | |
document.field("properties", props, OType.EMBEDDEDSET); | |
if (superClasses.isEmpty()) { | |
// Single super class is deprecated! | |
document.field("superClass", null, OType.STRING); | |
document.field("superClasses", null, OType.EMBEDDEDLIST); | |
} else { | |
// Single super class is deprecated! | |
document.field("superClass", superClasses.get(0).getName(), OType.STRING); | |
List<String> superClassesNames = new ArrayList<String>(); | |
for (OClassImpl superClass : superClasses) { | |
superClassesNames.add(superClass.getName()); | |
} | |
document.field("superClasses", superClassesNames, OType.EMBEDDEDLIST); | |
} | |
document.field("customFields", customFields != null && customFields.size() > 0 ? customFields : null, OType.EMBEDDEDMAP); | |
} finally { | |
document.setInternalStatus(ORecordElement.STATUS.LOADED); | |
} | |
return document; | |
} | |
@Override public int getShardForNewInstance(final ODocument doc) { | |
acquireSchemaReadLock(); | |
try { | |
return shardSelection.getShard(this, doc); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public int getDefaultShardId() { | |
acquireSchemaReadLock(); | |
try { | |
return defaultShardId; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public void setDefaultShardId(final int defaultShardId) { | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
this.defaultShardId = defaultShardId; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
public int[] getShardIds() { | |
acquireSchemaReadLock(); | |
try { | |
return shardIds; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public int[] getPolymorphicShardIds() { | |
acquireSchemaReadLock(); | |
try { | |
return Arrays.copyOf(polymorphicShardIds, polymorphicShardIds.length); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
private void setPolymorphicShardIds(final int[] iShardIds) { | |
Set<Integer> set = new TreeSet<Integer>(); | |
for (int iShardId : iShardIds) { | |
set.add(iShardId); | |
} | |
polymorphicShardIds = new int[set.size()]; | |
int i = 0; | |
for (Integer shardId : set) { | |
polymorphicShardIds[i] = shardId; | |
i++; | |
} | |
} | |
public void renameProperty(final String iOldName, final String iNewName) { | |
final OProperty p = properties.remove(iOldName.toLowerCase()); | |
if (p != null) | |
properties.put(iNewName.toLowerCase(), p); | |
} | |
public OClass addShardId(final int shardId) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
if (isAbstract()) { | |
throw new OSchemaException("Impossible to associate a shard to an abstract class class"); | |
} | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` addshard %d", name, shardId); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` addshard %d", name, shardId); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
addShardIdInternal(shardId); | |
} else | |
addShardIdInternal(shardId); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
public static OClass addShards(final OClass cls, final int iShards) { | |
final String shardBase = cls.getName().toLowerCase() + "_"; | |
for (int i = 1; i < iShards; ++i) { | |
cls.addShard(shardBase + i); | |
} | |
return cls; | |
} | |
@Override public OClass addShard(final String shardNameOrId) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
if (isAbstract()) { | |
throw new OSchemaException("Impossible to associate a shard to an abstract class class"); | |
} | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` addshard `%s`", name, shardNameOrId); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final int shardId = owner.createShardIfNeeded(shardNameOrId); | |
addShardIdInternal(shardId); | |
final String cmd = String.format("alter class `%s` addshard `%s`", name, shardNameOrId); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
} else { | |
final int shardId = owner.createShardIfNeeded(shardNameOrId); | |
addShardIdInternal(shardId); | |
} | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override public OClass truncateShard(String shardName) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.CLASS, ORole.PERMISSION_DELETE, name); | |
acquireSchemaReadLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("truncate shard %s", shardName); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("truncate shard %s", shardName); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
truncateShardInternal(shardName, storage); | |
} else | |
truncateShardInternal(shardName, storage); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
return this; | |
} | |
private void truncateShardInternal(final String shardName, final OStorage storage) { | |
final OShard shard = storage.getShardByName(shardName); | |
if (shard == null) { | |
throw new ODatabaseException("Shard with name " + shardName + " does not exist"); | |
} | |
try { | |
shard.truncate(); | |
} catch (IOException e) { | |
throw OException.wrapException(new ODatabaseException("Error during truncate of shard " + shardName), e); | |
} | |
for (OIndex index : getIndexes()) { | |
index.rebuild(); | |
} | |
} | |
public OClass removeShardId(final int shardId) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
if (shardIds.length == 1 && shardId == shardIds[0]) | |
throw new ODatabaseException(" Impossible to remove the last shard of class '" + getName() + "' drop the class instead"); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` removeshard %d", name, shardId); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` removeshard %d", name, shardId); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
removeShardIdInternal(shardId); | |
} else | |
removeShardIdInternal(shardId); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
public Collection<OClass> getSubclasses() { | |
acquireSchemaReadLock(); | |
try { | |
if (subclasses == null || subclasses.size() == 0) | |
return Collections.emptyList(); | |
return Collections.unmodifiableCollection(subclasses); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public Collection<OClass> getAllSubclasses() { | |
acquireSchemaReadLock(); | |
try { | |
final Set<OClass> set = new HashSet<OClass>(); | |
if (subclasses != null) { | |
set.addAll(subclasses); | |
for (OClass c : subclasses) | |
set.addAll(c.getAllSubclasses()); | |
} | |
return set; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Deprecated | |
public Collection<OClass> getBaseClasses() { | |
return getSubclasses(); | |
} | |
@Deprecated | |
public Collection<OClass> getAllBaseClasses() { | |
return getAllSubclasses(); | |
} | |
@Override | |
public Collection<OClass> getAllSuperClasses() { | |
Set<OClass> ret = new HashSet<OClass>(); | |
getAllSuperClasses(ret); | |
return ret; | |
} | |
private void getAllSuperClasses(Set<OClass> set) { | |
set.addAll(superClasses); | |
for (OClassImpl superClass : superClasses) { | |
superClass.getAllSuperClasses(set); | |
} | |
} | |
OClass removeBaseClassInternal(final OClass baseClass) { | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
if (subclasses == null) | |
return this; | |
if (subclasses.remove(baseClass)) | |
removePolymorphicShardIds((OClassImpl) baseClass); | |
return this; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
public float getOverSize() { | |
acquireSchemaReadLock(); | |
try { | |
if (overSize > 0) | |
// CUSTOM OVERSIZE SET | |
return overSize; | |
// NO OVERSIZE by default | |
float maxOverSize = 0; | |
float thisOverSize; | |
for (OClassImpl superClass : superClasses) { | |
thisOverSize = superClass.getOverSize(); | |
if (thisOverSize > maxOverSize) | |
maxOverSize = thisOverSize; | |
} | |
return maxOverSize; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public OClass setOverSize(final float overSize) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
// FORMAT FLOAT LOCALE AGNOSTIC | |
final String cmd = String.format("alter class `%s` oversize %s", name, new Float(overSize).toString()); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
// FORMAT FLOAT LOCALE AGNOSTIC | |
final String cmd = String.format("alter class `%s` oversize %s", name, new Float(overSize).toString()); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
setOverSizeInternal(overSize); | |
} else | |
setOverSizeInternal(overSize); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
@Override public float getClassOverSize() { | |
acquireSchemaReadLock(); | |
try { | |
return overSize; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public boolean isAbstract() { | |
acquireSchemaReadLock(); | |
try { | |
return abstractClass; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public OClass setAbstract(boolean isAbstract) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` abstract %s", name, isAbstract); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` abstract %s", name, isAbstract); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
setAbstractInternal(isAbstract); | |
} else | |
setAbstractInternal(isAbstract); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
public boolean isStrictMode() { | |
acquireSchemaReadLock(); | |
try { | |
return strictMode; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public OClass setStrictMode(final boolean isStrict) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` strictmode %s", name, isStrict); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` strictmode %s", name, isStrict); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
setStrictModeInternal(isStrict); | |
} else | |
setStrictModeInternal(isStrict); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
@Override public String toString() { | |
acquireSchemaReadLock(); | |
try { | |
return name; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Override public boolean equals(Object obj) { | |
acquireSchemaReadLock(); | |
try { | |
if (this == obj) | |
return true; | |
if (obj == null) | |
return false; | |
if (!OClass.class.isAssignableFrom(obj.getClass())) | |
return false; | |
final OClass other = (OClass) obj; | |
if (name == null) { | |
if (other.getName() != null) | |
return false; | |
} else if (!name.equals(other.getName())) | |
return false; | |
return true; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Override public int hashCode() { | |
int sh = hashCode; | |
if (sh != 0) | |
return sh; | |
acquireSchemaReadLock(); | |
try { | |
sh = hashCode; | |
if (sh != 0) | |
return sh; | |
calculateHashCode(); | |
return hashCode; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public int compareTo(final OClass o) { | |
acquireSchemaReadLock(); | |
try { | |
return name.compareTo(o.getName()); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public long count() { | |
return count(true); | |
} | |
public long count(final boolean isPolymorphic) { | |
acquireSchemaReadLock(); | |
try { | |
if (isPolymorphic) | |
return getDatabase().countShardElements(readableShards(getDatabase(), polymorphicShardIds)); | |
return getDatabase().countShardElements(readableShards(getDatabase(), shardIds)); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
/** | |
* Truncates all the shards the class uses. | |
* | |
* @throws IOException | |
*/ | |
public void truncate() throws IOException { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.CLASS, ORole.PERMISSION_UPDATE); | |
if (isSubClassOf(OSecurityShared.RESTRICTED_CLASSNAME)) { | |
throw new OSecurityException( | |
"Class '" + getName() + "' cannot be truncated because has record level security enabled (extends '" | |
+ OSecurityShared.RESTRICTED_CLASSNAME + "')"); | |
} | |
final OStorage storage = getDatabase().getStorage(); | |
acquireSchemaReadLock(); | |
try { | |
for (int id : shardIds) | |
storage.getShardById(id).truncate(); | |
for (OIndex<?> index : getClassIndexes()) | |
index.clear(); | |
Set<OIndex<?>> superclassIndexes = new HashSet<OIndex<?>>(); | |
superclassIndexes.addAll(getIndexes()); | |
superclassIndexes.removeAll(getClassIndexes()); | |
for (OIndex index : superclassIndexes) { | |
index.rebuild(); | |
} | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
/** | |
* Check if the current instance extends specified schema class. | |
* | |
* @param iClassName of class that should be checked | |
* @return Returns true if the current instance extends the passed schema class (iClass) | |
* @see #isSuperClassOf(OClass) | |
*/ | |
public boolean isSubClassOf(final String iClassName) { | |
acquireSchemaReadLock(); | |
try { | |
if (iClassName == null) | |
return false; | |
if (iClassName.equalsIgnoreCase(getName()) || iClassName.equalsIgnoreCase(getShortName())) | |
return true; | |
for (OClassImpl superClass : superClasses) { | |
if (superClass.isSubClassOf(iClassName)) | |
return true; | |
} | |
return false; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
/** | |
* Check if the current instance extends specified schema class. | |
* | |
* @param clazz to check | |
* @return true if the current instance extends the passed schema class (iClass) | |
* @see #isSuperClassOf(OClass) | |
*/ | |
public boolean isSubClassOf(final OClass clazz) { | |
acquireSchemaReadLock(); | |
try { | |
if (clazz == null) | |
return false; | |
if (equals(clazz)) | |
return true; | |
for (OClassImpl superClass : superClasses) { | |
if (superClass.isSubClassOf(clazz)) | |
return true; | |
} | |
return false; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
/** | |
* Returns true if the passed schema class (iClass) extends the current instance. | |
* | |
* @param clazz to check | |
* @return Returns true if the passed schema class extends the current instance | |
* @see #isSubClassOf(OClass) | |
*/ | |
public boolean isSuperClassOf(final OClass clazz) { | |
return clazz != null && clazz.isSubClassOf(this); | |
} | |
public Object get(final ATTRIBUTES iAttribute) { | |
if (iAttribute == null) | |
throw new IllegalArgumentException("attribute is null"); | |
switch (iAttribute) { | |
case NAME: | |
return getName(); | |
case SHORTNAME: | |
return getShortName(); | |
case SUPERCLASS: | |
return getSuperClass(); | |
case SUPERCLASSES: | |
return getSuperClasses(); | |
case OVERSIZE: | |
return getOverSize(); | |
case STRICTMODE: | |
return isStrictMode(); | |
case ABSTRACT: | |
return isAbstract(); | |
case SHARDSELECTION: | |
return getShardSelection(); | |
case CUSTOM: | |
return getCustomInternal(); | |
case DESCRIPTION: | |
return getDescription(); | |
} | |
throw new IllegalArgumentException("Cannot find attribute '" + iAttribute + "'"); | |
} | |
public OClass set(final ATTRIBUTES attribute, final Object iValue) { | |
if (attribute == null) | |
throw new IllegalArgumentException("attribute is null"); | |
final String stringValue = iValue != null ? iValue.toString() : null; | |
final boolean isNull = stringValue == null || stringValue.equalsIgnoreCase("NULL"); | |
switch (attribute) { | |
case NAME: | |
setName(decodeClassName(stringValue)); | |
break; | |
case SHORTNAME: | |
setShortName(decodeClassName(stringValue)); | |
break; | |
case SUPERCLASS: | |
if (stringValue == null) | |
throw new IllegalArgumentException("Superclass is null"); | |
if (stringValue.startsWith("+")) { | |
addSuperClass(getDatabase().getMetadata().getSchema().getClass(decodeClassName(stringValue.substring(1)))); | |
} else if (stringValue.startsWith("-")) { | |
removeSuperClass(getDatabase().getMetadata().getSchema().getClass(decodeClassName(stringValue.substring(1)))); | |
} else { | |
setSuperClass(getDatabase().getMetadata().getSchema().getClass(decodeClassName(stringValue))); | |
} | |
break; | |
case SUPERCLASSES: | |
setSuperClassesByNames(stringValue != null ? Arrays.asList(stringValue.split(",\\s*")) : null); | |
break; | |
case OVERSIZE: | |
setOverSize(Float.parseFloat(stringValue)); | |
break; | |
case STRICTMODE: | |
setStrictMode(Boolean.parseBoolean(stringValue)); | |
break; | |
case ABSTRACT: | |
setAbstract(Boolean.parseBoolean(stringValue)); | |
break; | |
case ADDSHARD: { | |
addShard(stringValue); | |
break; | |
} | |
case REMOVESHARD: | |
int clId = owner.getShardId(stringValue); | |
if (clId == NOT_EXISTENT_SHARD_ID) | |
throw new IllegalArgumentException("Shard id '" + stringValue + "' cannot be removed"); | |
removeShardId(clId); | |
break; | |
case SHARDSELECTION: | |
setShardSelection(stringValue); | |
break; | |
case CUSTOM: | |
int indx = stringValue != null ? stringValue.indexOf('=') : -1; | |
if (indx < 0) { | |
if (isNull || "clear".equalsIgnoreCase(stringValue)) { | |
clearCustom(); | |
} else | |
throw new IllegalArgumentException("Syntax error: expected <name> = <value> or clear, instead found: " + iValue); | |
} else { | |
String customName = stringValue.substring(0, indx).trim(); | |
String customValue = stringValue.substring(indx + 1).trim(); | |
if (isQuoted(customValue)) { | |
customValue = removeQuotes(customValue); | |
} | |
if (customValue.isEmpty()) | |
removeCustom(customName); | |
else | |
setCustom(customName, customValue); | |
} | |
break; | |
case DESCRIPTION: | |
setDescription(stringValue); | |
break; | |
case ENCRYPTION: | |
setEncryption(stringValue); | |
break; | |
} | |
return this; | |
} | |
private String removeQuotes(String s) { | |
s = s.trim(); | |
return s.substring(1, s.length() - 1); | |
} | |
private boolean isQuoted(String s) { | |
s = s.trim(); | |
if (s.startsWith("\"") && s.endsWith("\"")) | |
return true; | |
if (s.startsWith("'") && s.endsWith("'")) | |
return true; | |
if (s.startsWith("`") && s.endsWith("`")) | |
return true; | |
return false; | |
} | |
public OClassImpl setEncryption(final String iValue) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
final String cmd = String.format("alter class `%s` encryption %s", name, iValue); | |
database.command(new OCommandSQL(cmd)).execute(); | |
} else if (isDistributedCommand()) { | |
final String cmd = String.format("alter class `%s` encryption %s", name, iValue); | |
final OCommandSQL commandSQL = new OCommandSQL(cmd); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
setEncryptionInternal(iValue); | |
} else | |
setEncryptionInternal(iValue); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
protected void setEncryptionInternal(final String iValue) { | |
for (int cl : getShardIds()) { | |
final OShard c = getDatabase().getStorage().getShardById(cl); | |
if (c != null) | |
try { | |
c.set(OShard.ATTRIBUTES.ENCRYPTION, iValue); | |
} catch (IOException e) { | |
} | |
} | |
} | |
public OPropertyImpl addPropertyInternal(final String name, final OType type, final OType linkedType, final OClass linkedClass, | |
final boolean unsafe) { | |
if (name == null || name.length() == 0) | |
throw new OSchemaException("Found property name null"); | |
if (!unsafe) | |
checkPersistentPropertyType(getDatabase(), name, type); | |
final String lowerName = name.toLowerCase(); | |
final OPropertyImpl prop; | |
// This check are doubled becouse used by sql commands | |
if (linkedType != null) | |
OPropertyImpl.checkLinkTypeSupport(type); | |
if (linkedClass != null) | |
OPropertyImpl.checkSupportLinkedClass(type); | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
if (properties.containsKey(lowerName)) | |
throw new OSchemaException("Class '" + this.name + "' already has property '" + name + "'"); | |
OGlobalProperty global = owner.findOrCreateGlobalProperty(name, type); | |
prop = new OPropertyImpl(this, global); | |
properties.put(lowerName, prop); | |
if (linkedType != null) | |
prop.setLinkedTypeInternal(linkedType); | |
else if (linkedClass != null) | |
prop.setLinkedClassInternal(linkedClass); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
if (prop != null && !unsafe) | |
fireDatabaseMigration(getDatabase(), name, type); | |
return prop; | |
} | |
public OIndex<?> createIndex(final String iName, final INDEX_TYPE iType, final String... fields) { | |
return createIndex(iName, iType.name(), fields); | |
} | |
public OIndex<?> createIndex(final String iName, final String iType, final String... fields) { | |
return createIndex(iName, iType, null, null, fields); | |
} | |
public OIndex<?> createIndex(final String iName, final INDEX_TYPE iType, final OProgressListener iProgressListener, | |
final String... fields) { | |
return createIndex(iName, iType.name(), iProgressListener, null, fields); | |
} | |
public OIndex<?> createIndex(String iName, String iType, OProgressListener iProgressListener, ODocument metadata, | |
String... fields) { | |
return createIndex(iName, iType, iProgressListener, metadata, null, fields); | |
} | |
public OIndex<?> createIndex(final String name, String type, final OProgressListener progressListener, ODocument metadata, | |
String algorithm, final String... fields) { | |
if (type == null) | |
throw new IllegalArgumentException("Index type is null"); | |
type = type.toUpperCase(); | |
if (fields.length == 0) { | |
throw new OIndexException("List of fields to index cannot be empty."); | |
} | |
final String localName = this.name; | |
final int[] localPolymorphicShardIds = polymorphicShardIds; | |
for (final String fieldToIndex : fields) { | |
final String fieldName = decodeClassName(OIndexDefinitionFactory.extractFieldName(fieldToIndex)); | |
if (!fieldName.equals("@rid") && !existsProperty(fieldName)) | |
throw new OIndexException( | |
"Index with name '" + name + "' cannot be created on class '" + localName + "' because the field '" + fieldName | |
+ "' is absent in class definition"); | |
} | |
final OIndexDefinition indexDefinition = OIndexDefinitionFactory | |
.createIndexDefinition(this, Arrays.asList(fields), extractFieldTypes(fields), null, type, algorithm); | |
return getDatabase().getMetadata().getIndexManager() | |
.createIndex(name, type, indexDefinition, localPolymorphicShardIds, progressListener, metadata, algorithm); | |
} | |
public boolean areIndexed(final String... fields) { | |
return areIndexed(Arrays.asList(fields)); | |
} | |
public boolean areIndexed(final Collection<String> fields) { | |
final OIndexManager indexManager = getDatabase().getMetadata().getIndexManager(); | |
acquireSchemaReadLock(); | |
try { | |
final boolean currentClassResult = indexManager.areIndexed(name, fields); | |
if (currentClassResult) | |
return true; | |
for (OClassImpl superClass : superClasses) { | |
if (superClass.areIndexed(fields)) | |
return true; | |
} | |
return false; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public Set<OIndex<?>> getInvolvedIndexes(final String... fields) { | |
return getInvolvedIndexes(Arrays.asList(fields)); | |
} | |
public Set<OIndex<?>> getInvolvedIndexes(final Collection<String> fields) { | |
acquireSchemaReadLock(); | |
try { | |
final Set<OIndex<?>> result = new HashSet<OIndex<?>>(getClassInvolvedIndexes(fields)); | |
for (OClassImpl superClass : superClasses) { | |
result.addAll(superClass.getInvolvedIndexes(fields)); | |
} | |
return result; | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public Set<OIndex<?>> getClassInvolvedIndexes(final Collection<String> fields) { | |
final OIndexManager indexManager = getDatabase().getMetadata().getIndexManager(); | |
acquireSchemaReadLock(); | |
try { | |
return indexManager.getClassInvolvedIndexes(name, fields); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public Set<OIndex<?>> getClassInvolvedIndexes(final String... fields) { | |
return getClassInvolvedIndexes(Arrays.asList(fields)); | |
} | |
public OIndex<?> getClassIndex(final String name) { | |
acquireSchemaReadLock(); | |
try { | |
return getDatabase().getMetadata().getIndexManager().getClassIndex(this.name, name); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public Set<OIndex<?>> getClassIndexes() { | |
acquireSchemaReadLock(); | |
try { | |
final OIndexManagerProxy idxManager = getDatabase().getMetadata().getIndexManager(); | |
if (idxManager == null) | |
return new HashSet<OIndex<?>>(); | |
return idxManager.getClassIndexes(name); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Override public void getClassIndexes(final Collection<OIndex<?>> indexes) { | |
acquireSchemaReadLock(); | |
try { | |
final OIndexManagerProxy idxManager = getDatabase().getMetadata().getIndexManager(); | |
if (idxManager == null) | |
return; | |
idxManager.getClassIndexes(name, indexes); | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
@Override public OIndex<?> getAutoShardingIndex() { | |
final ODatabaseDocumentInternal db = ODatabaseRecordThreadLocal.INSTANCE.getIfDefined(); | |
return db != null ? db.getMetadata().getIndexManager().getClassAutoShardingIndex(name) : null; | |
} | |
@Override public boolean isEdgeType() { | |
return isSubClassOf(EDGE_CLASS_NAME); | |
} | |
@Override public boolean isVertexType() { | |
return isSubClassOf(VERTEX_CLASS_NAME); | |
} | |
public void onPostIndexManagement() { | |
final OIndex<?> autoShardingIndex = getAutoShardingIndex(); | |
if (autoShardingIndex != null) { | |
if (!getDatabase().getStorage().isRemote()) { | |
// OVERRIDE SHARD SELECTION | |
acquireSchemaWriteLock(); | |
try { | |
this.shardSelection = new OAutoShardingShardSelectionStrategy(this, autoShardingIndex); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
} else if (shardSelection instanceof OAutoShardingShardSelectionStrategy) { | |
// REMOVE AUTO SHARDING SHARD SELECTION | |
acquireSchemaWriteLock(); | |
try { | |
this.shardSelection = owner.getShardSelectionFactory().newInstanceOfDefaultClass(); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
} | |
@Override public void getIndexes(final Collection<OIndex<?>> indexes) { | |
acquireSchemaReadLock(); | |
try { | |
getClassIndexes(indexes); | |
for (OClass superClass : superClasses) { | |
superClass.getIndexes(indexes); | |
} | |
} finally { | |
releaseSchemaReadLock(); | |
} | |
} | |
public Set<OIndex<?>> getIndexes() { | |
final Set<OIndex<?>> indexes = new HashSet<OIndex<?>>(); | |
getIndexes(indexes); | |
return indexes; | |
} | |
public void acquireSchemaReadLock() { | |
owner.acquireSchemaReadLock(); | |
} | |
public void releaseSchemaReadLock() { | |
owner.releaseSchemaReadLock(); | |
} | |
public void acquireSchemaWriteLock() { | |
owner.acquireSchemaWriteLock(); | |
} | |
public void releaseSchemaWriteLock() { | |
releaseSchemaWriteLock(true); | |
} | |
public void releaseSchemaWriteLock(final boolean iSave) { | |
calculateHashCode(); | |
owner.releaseSchemaWriteLock(iSave); | |
} | |
public void checkEmbedded() { | |
owner.checkEmbedded(getDatabase().getStorage().getUnderlying().getUnderlying()); | |
} | |
public void setShardSelectionInternal(final String shardSelection) { | |
// AVOID TO CHECK THIS IN LOCK TO AVOID RE-GENERATION OF IMMUTABLE SCHEMAS | |
if (this.shardSelection.getName().equals(shardSelection)) | |
// NO CHANGES | |
return; | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
this.shardSelection = owner.getShardSelectionFactory().newInstance(shardSelection); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
public void setShardSelectionInternal(final OShardSelectionStrategy iShardSelection) { | |
// AVOID TO CHECK THIS IN LOCK TO AVOID RE-GENERATION OF IMMUTABLE SCHEMAS | |
if (this.shardSelection.getClass().equals(iShardSelection.getClass())) | |
// NO CHANGES | |
return; | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
this.shardSelection = iShardSelection; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
public void fireDatabaseMigration(final ODatabaseDocument database, final String propertyName, final OType type) { | |
final boolean strictSQL = ((ODatabaseInternal) database).getStorage().getConfiguration().isStrictSql(); | |
database.query(new OSQLAsynchQuery<Object>( | |
"select from " + getEscapedName(name, strictSQL) + " where " + getEscapedName(propertyName, strictSQL) + ".type() <> \"" | |
+ type.name() + "\"", new OCommandResultListener() { | |
@Override public boolean result(Object iRecord) { | |
final ODocument record = ((OIdentifiable) iRecord).getRecord(); | |
record.field(propertyName, record.field(propertyName), type); | |
database.save(record); | |
return true; | |
} | |
@Override public void end() { | |
} | |
@Override public Object getResult() { | |
return null; | |
} | |
})); | |
} | |
public void firePropertyNameMigration(final ODatabaseDocument database, final String propertyName, final String newPropertyName, | |
final OType type) { | |
final boolean strictSQL = ((ODatabaseInternal) database).getStorage().getConfiguration().isStrictSql(); | |
database.query(new OSQLAsynchQuery<Object>( | |
"select from " + getEscapedName(name, strictSQL) + " where " + getEscapedName(propertyName, strictSQL) + " is not null ", | |
new OCommandResultListener() { | |
@Override public boolean result(Object iRecord) { | |
final ODocument record = ((OIdentifiable) iRecord).getRecord(); | |
record.setFieldType(propertyName, type); | |
record.field(newPropertyName, record.field(propertyName), type); | |
database.save(record); | |
return true; | |
} | |
@Override public void end() { | |
} | |
@Override public Object getResult() { | |
return null; | |
} | |
})); | |
} | |
public void checkPersistentPropertyType(final ODatabaseInternal<ORecord> database, final String propertyName, final OType type) { | |
final boolean strictSQL = database.getStorage().getConfiguration().isStrictSql(); | |
final StringBuilder builder = new StringBuilder(256); | |
builder.append("select count(*) from "); | |
builder.append(getEscapedName(name, strictSQL)); | |
builder.append(" where "); | |
builder.append(getEscapedName(propertyName, strictSQL)); | |
builder.append(".type() not in ["); | |
final Iterator<OType> cur = type.getCastable().iterator(); | |
while (cur.hasNext()) { | |
builder.append('"').append(cur.next().name()).append('"'); | |
if (cur.hasNext()) | |
builder.append(","); | |
} | |
builder.append("] and ").append(getEscapedName(propertyName, strictSQL)).append(" is not null "); | |
if (type.isMultiValue()) | |
builder.append(" and ").append(getEscapedName(propertyName, strictSQL)).append(".size() <> 0 limit 1"); | |
final List<ODocument> res = database.command(new OCommandSQL(builder.toString())).execute(); | |
if (((Long) res.get(0).field("count")) > 0) | |
throw new OSchemaException("The database contains some schema-less data in the property '" + name + "." + propertyName | |
+ "' that is not compatible with the type " + type + ". Fix those records and change the schema again"); | |
} | |
protected String getEscapedName(final String iName, final boolean iStrictSQL) { | |
if (iStrictSQL) | |
// ESCAPE NAME | |
return "`" + iName + "`"; | |
return iName; | |
} | |
public OSchemaShared getOwner() { | |
return owner; | |
} | |
private void calculateHashCode() { | |
int result = super.hashCode(); | |
result = 31 * result + (name != null ? name.hashCode() : 0); | |
hashCode = result; | |
} | |
private void setOverSizeInternal(final float overSize) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
this.overSize = overSize; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private void setCustomInternal(final String name, final String value) { | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
if (customFields == null) | |
customFields = new HashMap<String, String>(); | |
if (value == null || "null".equalsIgnoreCase(value)) | |
customFields.remove(name); | |
else | |
customFields.put(name, value); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private void clearCustomInternal() { | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
customFields = null; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private void setNameInternal(final String name) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
final String oldName = this.name; | |
owner.changeClassName(this.name, name, this); | |
this.name = name; | |
ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (!database.getStorageVersions().classesAreDetectedByShardId()) { | |
for (int shardId : shardIds) { | |
long[] range = storage.getShardDataRange(shardId); | |
OPhysicalPosition[] positions = storage.ceilingPhysicalPositions(shardId, new OPhysicalPosition(range[0])); | |
do { | |
for (OPhysicalPosition position : positions) { | |
final ORecordId identity = new ORecordId(shardId, position.shardPosition); | |
final ORawBuffer record = storage.readRecord(identity, null, true, null).getResult(); | |
if (record.recordType == ODocument.RECORD_TYPE) { | |
final ORecordSerializerSchemaAware2CSV serializer = (ORecordSerializerSchemaAware2CSV) ORecordSerializerFactory | |
.instance().getFormat(ORecordSerializerSchemaAware2CSV.NAME); | |
String persName = new String(record.buffer, "UTF-8"); | |
if (serializer.getClassName(persName).equalsIgnoreCase(name)) { | |
final ODocument document = new ODocument(); | |
document.setLazyLoad(false); | |
document.fromStream(record.buffer); | |
ORecordInternal.setVersion(document, record.version); | |
ORecordInternal.setIdentity(document, identity); | |
document.setClassName(name); | |
document.setDirty(); | |
document.save(); | |
} | |
} | |
if (positions.length > 0) | |
positions = storage.higherPhysicalPositions(shardId, positions[positions.length - 1]); | |
} | |
} while (positions.length > 0); | |
} | |
} | |
renameShard(oldName, this.name); | |
} catch (UnsupportedEncodingException e) { | |
throw OException.wrapException(new OSchemaException("Error reading schema"), e); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private void renameShard(String oldName, String newName) { | |
oldName = oldName.toLowerCase(); | |
newName = newName.toLowerCase(); | |
final ODatabaseDocumentInternal database = getDatabase(); | |
final OStorage storage = database.getStorage(); | |
if (storage.getShardIdByName(newName) != -1) | |
return; | |
final int shardId = storage.getShardIdByName(oldName); | |
if (shardId == -1) | |
return; | |
if (!hasShardId(shardId)) | |
return; | |
database.command(new OCommandSQL("alter shard `" + oldName + "` name `" + newName + "`")).execute(); | |
} | |
private void setShortNameInternal(final String iShortName) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
String oldName = null; | |
if (this.shortName != null) | |
oldName = this.shortName; | |
owner.changeClassName(oldName, iShortName, this); | |
this.shortName = iShortName; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private void setDescriptionInternal(final String iDescription) { | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
this.description = iDescription; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private void dropPropertyInternal(final String iPropertyName) { | |
if (getDatabase().getTransaction().isActive()) | |
throw new IllegalStateException("Cannot drop a property inside a transaction"); | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_DELETE); | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
final OProperty prop = properties.remove(iPropertyName.toLowerCase()); | |
if (prop == null) | |
throw new OSchemaException("Property '" + iPropertyName + "' not found in class " + name + "'"); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private OClass addShardIdInternal(final int shardId) { | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
owner.checkShardCanBeAdded(shardId, this); | |
for (int currId : shardIds) | |
if (currId == shardId) | |
// ALREADY ADDED | |
return this; | |
shardIds = OArrays.copyOf(shardIds, shardIds.length + 1); | |
shardIds[shardIds.length - 1] = shardId; | |
Arrays.sort(shardIds); | |
addPolymorphicShardId(shardId); | |
if (defaultShardId == NOT_EXISTENT_SHARD_ID) | |
defaultShardId = shardId; | |
owner.addShardForClass(shardId, this); | |
return this; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private void addPolymorphicShardId(int shardId) { | |
if (Arrays.binarySearch(polymorphicShardIds, shardId) >= 0) | |
return; | |
polymorphicShardIds = OArrays.copyOf(polymorphicShardIds, polymorphicShardIds.length + 1); | |
polymorphicShardIds[polymorphicShardIds.length - 1] = shardId; | |
Arrays.sort(polymorphicShardIds); | |
addShardIdToIndexes(shardId); | |
for (OClassImpl superClass : superClasses) { | |
superClass.addPolymorphicShardId(shardId); | |
} | |
} | |
private OClass removeShardIdInternal(final int shardToRemove) { | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
boolean found = false; | |
for (int shardId : shardIds) { | |
if (shardId == shardToRemove) { | |
found = true; | |
break; | |
} | |
} | |
if (found) { | |
final int[] newShardIds = new int[shardIds.length - 1]; | |
for (int i = 0, k = 0; i < shardIds.length; ++i) { | |
if (shardIds[i] == shardToRemove) | |
// JUMP IT | |
continue; | |
newShardIds[k] = shardIds[i]; | |
k++; | |
} | |
shardIds = newShardIds; | |
removePolymorphicShardId(shardToRemove); | |
} | |
if (defaultShardId == shardToRemove) { | |
if (shardIds.length >= 1) | |
defaultShardId = shardIds[0]; | |
else | |
defaultShardId = NOT_EXISTENT_SHARD_ID; | |
} | |
owner.removeShardForClass(shardToRemove, this); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
return this; | |
} | |
private void setAbstractInternal(final boolean isAbstract) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
if (isAbstract) { | |
// SWITCH TO ABSTRACT | |
if (defaultShardId != NOT_EXISTENT_SHARD_ID) { | |
// CHECK | |
if (count() > 0) | |
throw new IllegalStateException("Cannot set the class as abstract because contains records."); | |
tryDropShard(defaultShardId); | |
for (int shardId : getShardIds()) { | |
tryDropShard(shardId); | |
removePolymorphicShardId(shardId); | |
owner.removeShardForClass(shardId, this); | |
} | |
setShardIds(new int[] { NOT_EXISTENT_SHARD_ID }); | |
defaultShardId = NOT_EXISTENT_SHARD_ID; | |
} | |
} else { | |
if (!abstractClass) | |
return; | |
int shardId = getDatabase().getShardIdByName(name); | |
if (shardId == -1) | |
shardId = getDatabase().addShard(name); | |
this.defaultShardId = shardId; | |
this.shardIds[0] = this.defaultShardId; | |
this.polymorphicShardIds = Arrays.copyOf(shardIds, shardIds.length); | |
for (OClass clazz : getAllSubclasses()) { | |
if (clazz instanceof OClassImpl) { | |
addPolymorphicShardIds((OClassImpl) clazz); | |
} else { | |
OLogManager.instance().warn(this, "Warning: cannot set polymorphic shard IDs for class " + name); | |
} | |
} | |
} | |
this.abstractClass = isAbstract; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private void setStrictModeInternal(final boolean iStrict) { | |
getDatabase().checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
acquireSchemaWriteLock(); | |
try { | |
checkEmbedded(); | |
this.strictMode = iStrict; | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private OProperty addProperty(final String propertyName, final OType type, final OType linkedType, final OClass linkedClass, | |
final boolean unsafe) { | |
if (type == null) | |
throw new OSchemaException("Property type not defined."); | |
if (propertyName == null || propertyName.length() == 0) | |
throw new OSchemaException("Property name is null or empty"); | |
if (getDatabase().getStorage().getConfiguration().isStrictSql()) { | |
validatePropertyName(propertyName); | |
} | |
if (getDatabase().getTransaction().isActive()) | |
throw new OSchemaException("Cannot create property '" + propertyName + "' inside a transaction"); | |
final ODatabaseDocumentInternal database = getDatabase(); | |
database.checkSecurity(ORule.ResourceGeneric.SCHEMA, ORole.PERMISSION_UPDATE); | |
if (linkedType != null) | |
OPropertyImpl.checkLinkTypeSupport(type); | |
if (linkedClass != null) | |
OPropertyImpl.checkSupportLinkedClass(type); | |
acquireSchemaWriteLock(); | |
try { | |
final StringBuilder cmd = new StringBuilder("create property "); | |
// CLASS.PROPERTY NAME | |
if (getDatabase().getStorage().getConfiguration().isStrictSql()) | |
cmd.append('`'); | |
cmd.append(name); | |
if (getDatabase().getStorage().getConfiguration().isStrictSql()) | |
cmd.append('`'); | |
cmd.append('.'); | |
if (getDatabase().getStorage().getConfiguration().isStrictSql()) | |
cmd.append('`'); | |
cmd.append(propertyName); | |
if (getDatabase().getStorage().getConfiguration().isStrictSql()) | |
cmd.append('`'); | |
// TYPE | |
cmd.append(' '); | |
cmd.append(type.name); | |
if (linkedType != null) { | |
// TYPE | |
cmd.append(' '); | |
cmd.append(linkedType.name); | |
} else if (linkedClass != null) { | |
// TYPE | |
cmd.append(' '); | |
if (getDatabase().getStorage().getConfiguration().isStrictSql()) | |
cmd.append('`'); | |
cmd.append(linkedClass.getName()); | |
if (getDatabase().getStorage().getConfiguration().isStrictSql()) | |
cmd.append('`'); | |
} | |
if (unsafe) | |
cmd.append(" unsafe "); | |
final OStorage storage = database.getStorage(); | |
if (storage instanceof OStorageProxy) { | |
database.command(new OCommandSQL(cmd.toString())).execute(); | |
reload(); | |
return getProperty(propertyName); | |
} else if (isDistributedCommand()) { | |
final OCommandSQL commandSQL = new OCommandSQL(cmd.toString()); | |
commandSQL.addExcludedNode(((OAutoshardedStorage) storage).getNodeId()); | |
database.command(commandSQL).execute(); | |
return (OProperty) OScenarioThreadLocal.executeAsDistributed(new Callable<OProperty>() { | |
@Override public OProperty call() throws Exception { | |
return addPropertyInternal(propertyName, type, linkedType, linkedClass, unsafe); | |
} | |
}); | |
} else | |
return (OProperty) OScenarioThreadLocal.executeAsDistributed(new Callable<OProperty>() { | |
@Override public OProperty call() throws Exception { | |
return addPropertyInternal(propertyName, type, linkedType, linkedClass, unsafe); | |
} | |
}); | |
} finally { | |
releaseSchemaWriteLock(); | |
} | |
} | |
private void validatePropertyName(final String propertyName) { | |
} | |
private int getShardId(final String stringValue) { | |
int clId; | |
if (!stringValue.isEmpty() && Character.isDigit(stringValue.charAt(0))) | |
try { | |
clId = Integer.parseInt(stringValue); | |
} catch (NumberFormatException e) { | |
clId = getDatabase().getShardIdByName(stringValue); | |
} | |
else | |
clId = getDatabase().getShardIdByName(stringValue); | |
return clId; | |
} | |
private void addShardIdToIndexes(int iId) { | |
if (getDatabase().getStorage().getUnderlying() instanceof OAbstractPaginatedStorage) { | |
final String shardName = getDatabase().getShardNameById(iId); | |
final List<String> indexesToAdd = new ArrayList<String>(); | |
for (OIndex<?> index : getIndexes()) | |
indexesToAdd.add(index.getName()); | |
final OIndexManager indexManager = getDatabase().getMetadata().getIndexManager(); | |
for (String indexName : indexesToAdd) | |
indexManager.addShardToIndex(shardName, indexName); | |
} | |
} | |
/** | |
* Adds a base class to the current one. It adds also the base class shard ids to the polymorphic shard ids array. | |
* | |
* @param iBaseClass The base class to add. | |
*/ | |
private OClass addBaseClass(final OClassImpl iBaseClass) { | |
checkRecursion(iBaseClass); | |
if (subclasses == null) | |
subclasses = new ArrayList<OClass>(); | |
if (subclasses.contains(iBaseClass)) | |
return this; | |
subclasses.add(iBaseClass); | |
addPolymorphicShardIdsWithInheritance(iBaseClass); | |
return this; | |
} | |
private void checkParametersConflict(final OClass baseClass) { | |
final Collection<OProperty> baseClassProperties = baseClass.properties(); | |
for (OProperty property : baseClassProperties) { | |
OProperty thisProperty = getProperty(property.getName()); | |
if (thisProperty != null && !thisProperty.getType().equals(property.getType())) { | |
throw new OSchemaException( | |
"Cannot add base class '" + baseClass.getName() + "', because of property conflict: '" + thisProperty + "' vs '" | |
+ property + "'"); | |
} | |
} | |
} | |
protected static void checkParametersConflict(List<OClass> classes) { | |
final Map<String, OProperty> comulative = new HashMap<String, OProperty>(); | |
final Map<String, OProperty> properties = new HashMap<String, OProperty>(); | |
for (OClass superClass : classes) { | |
if (superClass == null) | |
continue; | |
OClassImpl impl; | |
if (superClass instanceof OClassAbstractDelegate) | |
impl = (OClassImpl) ((OClassAbstractDelegate) superClass).delegate; | |
else | |
impl = (OClassImpl) superClass; | |
impl.propertiesMap(properties, false); | |
for (Map.Entry<String, OProperty> entry : properties.entrySet()) { | |
if (comulative.containsKey(entry.getKey())) { | |
final String property = entry.getKey(); | |
final OProperty existingProperty = comulative.get(property); | |
if (!existingProperty.getType().equals(entry.getValue().getType())) { | |
throw new OSchemaException("Properties conflict detected: '" + existingProperty + "] vs [" + entry.getValue() + "]"); | |
} | |
} | |
} | |
comulative.putAll(properties); | |
properties.clear(); | |
} | |
} | |
private void checkRecursion(final OClass baseClass) { | |
if (isSubClassOf(baseClass)) { | |
throw new OSchemaException("Cannot add base class '" + baseClass.getName() + "', because of recursion"); | |
} | |
} | |
private void removePolymorphicShardIds(final OClassImpl iBaseClass) { | |
for (final int shardId : iBaseClass.polymorphicShardIds) | |
removePolymorphicShardId(shardId); | |
} | |
private void removePolymorphicShardId(final int shardId) { | |
final int index = Arrays.binarySearch(polymorphicShardIds, shardId); | |
if (index < 0) | |
return; | |
if (index < polymorphicShardIds.length - 1) | |
System.arraycopy(polymorphicShardIds, index + 1, polymorphicShardIds, index, polymorphicShardIds.length - (index + 1)); | |
polymorphicShardIds = Arrays.copyOf(polymorphicShardIds, polymorphicShardIds.length - 1); | |
removeShardFromIndexes(shardId); | |
for (OClassImpl superClass : superClasses) { | |
superClass.removePolymorphicShardId(shardId); | |
} | |
} | |
private void removeShardFromIndexes(final int iId) { | |
if (getDatabase().getStorage().getUnderlying() instanceof OAbstractPaginatedStorage) { | |
final String shardName = getDatabase().getShardNameById(iId); | |
final List<String> indexesToRemove = new ArrayList<String>(); | |
for (final OIndex<?> index : getIndexes()) | |
indexesToRemove.add(index.getName()); | |
final OIndexManager indexManager = getDatabase().getMetadata().getIndexManager(); | |
for (final String indexName : indexesToRemove) | |
indexManager.removeShardFromIndex(shardName, indexName); | |
} | |
} | |
private void tryDropShard(final int defaultShardId) { | |
if (name.toLowerCase().equals(getDatabase().getShardNameById(defaultShardId))) { | |
// DROP THE DEFAULT SHARD CALLED WITH THE SAME NAME ONLY IF EMPTY | |
if (getDatabase().getShardRecordSizeById(defaultShardId) == 0) | |
getDatabase().dropShard(defaultShardId, true); | |
} | |
} | |
private ODatabaseDocumentInternal getDatabase() { | |
return ODatabaseRecordThreadLocal.INSTANCE.get(); | |
} | |
/** | |
* Add different shard id to the "polymorphic shard ids" array. | |
*/ | |
private void addPolymorphicShardIds(final OClassImpl iBaseClass) { | |
Set<Integer> shards = new TreeSet<Integer>(); | |
for (int shardId : polymorphicShardIds) { | |
shards.add(shardId); | |
} | |
for (int shardId : iBaseClass.polymorphicShardIds) { | |
if (shards.add(shardId)) { | |
try { | |
addShardIdToIndexes(shardId); | |
} catch (RuntimeException e) { | |
OLogManager.instance().warn(this, "Error adding shardId '%d' to index of class '%s'", e, shardId, getName()); | |
shards.remove(shardId); | |
} | |
} | |
} | |
polymorphicShardIds = new int[shards.size()]; | |
int i = 0; | |
for (Integer shard : shards) { | |
polymorphicShardIds[i] = shard; | |
i++; | |
} | |
} | |
private void addPolymorphicShardIdsWithInheritance(final OClassImpl iBaseClass) { | |
addPolymorphicShardIds(iBaseClass); | |
for (OClassImpl superClass : superClasses) { | |
superClass.addPolymorphicShardIdsWithInheritance(iBaseClass); | |
} | |
} | |
public List<OType> extractFieldTypes(final String[] fieldNames) { | |
final List<OType> types = new ArrayList<OType>(fieldNames.length); | |
for (String fieldName : fieldNames) { | |
if (!fieldName.equals("@rid")) | |
types.add(getProperty(decodeClassName(OIndexDefinitionFactory.extractFieldName(fieldName)).toLowerCase()).getType()); | |
else | |
types.add(OType.LINK); | |
} | |
return types; | |
} | |
private OClass setShardIds(final int[] iShardIds) { | |
shardIds = iShardIds; | |
Arrays.sort(shardIds); | |
return this; | |
} | |
private boolean isDistributedCommand() { | |
return getDatabase().getStorage() instanceof OAutoshardedStorage && !OScenarioThreadLocal.INSTANCE.isRunModeDistributed(); | |
} | |
public static String decodeClassName(String s) { | |
if (s == null) { | |
return null; | |
} | |
s = s.trim(); | |
if (s.startsWith("`") && s.endsWith("`")) { | |
return s.substring(1, s.length() - 1); | |
} | |
return s; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment