Orientdb: OValidationException in OElement.save() for "read only field"

Created on 9 Oct 2018  路  6Comments  路  Source: orientechnologies/orientdb

OrientDB Version: 3.0.8

Java Version: Java8

OS: Linux

Expected behavior

We should be able to save elements with read only fields in a remote database.

Actual behavior

An OValidationException is thrown.

Steps to reproduce

First create a remote database (the error does not occur if running an in memory database). Then execute the code below against the remote database, modifying the URL and credentials as needed.

import java.util.HashMap;

import com.orientechnologies.orient.core.db.ODatabaseSession;
import com.orientechnologies.orient.core.db.OrientDB;
import com.orientechnologies.orient.core.db.OrientDBConfig;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OProperty;
import com.orientechnologies.orient.core.metadata.schema.OSchema;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.OElement;
import com.orientechnologies.orient.core.sql.executor.OResultSet;

public class SaveTest {

  public static void main(String[] args) {
    OrientDB odb = new OrientDB("remote:localhost", OrientDBConfig.defaultConfig());
    ODatabaseSession db = odb.open("test", "admin", "admin");

    OSchema schema = db.getMetadata().getSchema();

    OClass childClass = schema.createClass("Child");
    OProperty valProp = childClass.createProperty("val", OType.ANY);
    valProp.setReadonly(true);
    valProp.setMandatory(true);
    valProp.setNotNull(true);

    OClass parentClass = schema.createClass("Parent");
    OProperty idProp = parentClass.createProperty("id", OType.STRING);
    idProp.setReadonly(true);
    idProp.setMandatory(true);
    idProp.setNotNull(true);

    OProperty childProp = parentClass.createProperty("child", OType.LINK);
    childProp.setReadonly(true);
    childProp.setMandatory(true);
    childProp.setNotNull(true);

    db.getMetadata().reload();

    OElement child = db.newElement("Child");
    child.setProperty("val", 4);

    OElement parent = db.newElement("Parent");
    parent.setProperty("id", "myId");
    parent.setProperty("child", child, OType.LINK);

    parent.save();

    OResultSet resultSet = db.query("SELECT FROM Parent", new HashMap<>());
    OElement queried = resultSet.next().toElement();
    System.out.println(queried.toJSON());

    db.close();
    odb.close();
  }
}

This will produce the following exception:

Exception in thread "main" com.orientechnologies.orient.core.exception.OValidationException: The field 'Child.val' is immutable and cannot be altered. Field value is: 4
    DB name="test"
    Error Code="4"
    DB name="test"
    Error Code="4"
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at com.orientechnologies.orient.client.binary.OChannelBinaryAsynchClient.throwSerializedException(OChannelBinaryAsynchClient.java:318)
    at com.orientechnologies.orient.client.binary.OChannelBinaryAsynchClient.handleStatus(OChannelBinaryAsynchClient.java:275)
    at com.orientechnologies.orient.client.binary.OChannelBinaryAsynchClient.beginResponse(OChannelBinaryAsynchClient.java:191)
    at com.orientechnologies.orient.client.binary.OChannelBinaryAsynchClient.beginResponse(OChannelBinaryAsynchClient.java:153)
    at com.orientechnologies.orient.client.remote.OStorageRemote.beginResponse(OStorageRemote.java:1931)
    at com.orientechnologies.orient.client.remote.OStorageRemote.lambda$asyncNetworkOperationRetry$1(OStorageRemote.java:290)
    at com.orientechnologies.orient.client.remote.OStorageRemote.baseNetworkOperation(OStorageRemote.java:404)
    at com.orientechnologies.orient.client.remote.OStorageRemote.asyncNetworkOperationRetry(OStorageRemote.java:273)
    at com.orientechnologies.orient.client.remote.OStorageRemote.asyncNetworkOperationNoRetry(OStorageRemote.java:261)
    at com.orientechnologies.orient.client.remote.OStorageRemote.createRecord(OStorageRemote.java:716)
    at com.orientechnologies.orient.core.db.document.ODatabaseDocumentRemote.executeSaveRecord(ODatabaseDocumentRemote.java:586)
    at com.orientechnologies.orient.core.tx.OTransactionNoTx.saveNew(OTransactionNoTx.java:248)
    at com.orientechnologies.orient.core.tx.OTransactionNoTx.saveRecord(OTransactionNoTx.java:174)
    at com.orientechnologies.orient.core.db.document.ODatabaseDocumentAbstract.saveInternal(ODatabaseDocumentAbstract.java:2076)
    at com.orientechnologies.orient.core.db.document.ODatabaseDocumentAbstract.save(ODatabaseDocumentAbstract.java:2041)
    at com.orientechnologies.orient.core.db.document.ODatabaseDocumentAbstract.save(ODatabaseDocumentAbstract.java:84)
    at com.orientechnologies.orient.core.record.impl.ODocument.save(ODocument.java:2109)
    at com.orientechnologies.orient.core.record.impl.ODocument.save(ODocument.java:2100)
    at com.orientechnologies.orient.core.record.impl.ODocument.save(ODocument.java:63)
    at ODBTest$.delayedEndpoint$ODBTest$1(ODBTest.scala:46)
    at ODBTest$delayedInit$body.apply(ODBTest.scala:12)
    at scala.Function0.apply$mcV$sp(Function0.scala:34)
    at scala.Function0.apply$mcV$sp$(Function0.scala:34)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App.$anonfun$main$1$adapted(App.scala:76)
    at scala.collection.immutable.List.foreach(List.scala:389)
    at scala.App.main(App.scala:76)
    at scala.App.main$(App.scala:74)
    at ODBTest$.main(ODBTest.scala:12)
    at ODBTest.main(ODBTest.scala)
    Suppressed: com.orientechnologies.orient.core.exception.OValidationException: The field 'Child.val' is immutable and cannot be altered. Field value is: 4
    DB name="test"
    Error Code="4"
        at com.orientechnologies.orient.core.record.impl.ODocument.validateField(ODocument.java:699)
        at com.orientechnologies.orient.core.record.impl.ODocument.validate(ODocument.java:2365)
        at com.orientechnologies.orient.core.db.document.ODatabaseDocumentAbstract.saveInternal(ODatabaseDocumentAbstract.java:2056)
        at com.orientechnologies.orient.core.db.document.ODatabaseDocumentAbstract.save(ODatabaseDocumentAbstract.java:2041)
        at com.orientechnologies.orient.core.db.document.ODatabaseDocumentAbstract.save(ODatabaseDocumentAbstract.java:1933)
        at com.orientechnologies.orient.core.db.document.ODatabaseDocumentAbstract.save(ODatabaseDocumentAbstract.java:84)
        at com.orientechnologies.orient.server.OConnectionBinaryExecutor.executeCreateRecord(OConnectionBinaryExecutor.java:374)
        at com.orientechnologies.orient.client.remote.message.OCreateRecordRequest.execute(OCreateRecordRequest.java:118)
        at com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary.sessionRequest(ONetworkProtocolBinary.java:304)
        at com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary.execute(ONetworkProtocolBinary.java:206)
        at com.orientechnologies.common.thread.OSoftThread.run(OSoftThread.java:69)
bug

All 6 comments

The test case I provided was representative of the code that we have in our system. However, upon further investigation, it seems like the issues does not have to do with a link and a nested document. I have found a simpler set of code that reproduces the issue:

import java.util.HashMap;

import com.orientechnologies.orient.core.db.ODatabaseSession;
import com.orientechnologies.orient.core.db.OrientDB;
import com.orientechnologies.orient.core.db.OrientDBConfig;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OProperty;
import com.orientechnologies.orient.core.metadata.schema.OSchema;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.OElement;
import com.orientechnologies.orient.core.sql.executor.OResultSet;

public class SaveTest {

  public static final String ClassName = "TestClass";
  public static final String PropVal = "val";

  public static void main(String[] args) {
    final OrientDB odb = new OrientDB("remote:localhost", OrientDBConfig.defaultConfig());
    final ODatabaseSession db = odb.open("test", "admin", "admin");

    final OSchema schema = db.getMetadata().getSchema();

    if (schema.existsClass(ClassName)) {
      schema.dropClass(ClassName);
    }

    final OClass childClass = schema.createClass(ClassName);
    final OProperty valProp = childClass.createProperty(PropVal, OType.DOUBLE);
    valProp.setReadonly(true);
    valProp.setMandatory(true);
    valProp.setNotNull(true);

    db.getMetadata().reload();

    final OElement test = db.newElement("Child");
    test.setProperty(PropVal, 4);
    test.save();

    final OResultSet resultSet = db.query("SELECT FROM " + ClassName, new HashMap<>());
    final OElement queried = resultSet.next().toElement();
    System.out.println(queried.toJSON());

    db.close();
    odb.close();
  }
}

@luigidellaquila I was wondering if anyone was able to reproduce this issue using the code I provided. It seems like a pretty big issue, so I am curious if it is actually reproducible, or if I am doing something odd that is only affecting me.

Hi @mmacfadden

Sorry, we didn't have a chance to check it yet, I'll try to do it asap and I'll let you know

Thanks

Luigi

@luigidellaquila Thanks. I know you guys are pretty busy with all the feedback on 3.X. Keep up the good work. If you need any help on this one let me know. I can dig deeper if needed.

Hi @mmacfadden

I managed to reproduce and fix the problem, I'm pushing the fix now. It will be released with v 3.0.9

Thanks

Luigi

@luigidellaquila Thanks!

Was this page helpful?
0 / 5 - 0 ratings