9 Replies Latest reply: Mar 22, 2009 8:40 AM by Greybird-Oracle RSS

    Nullify of superclass field fails

    user593875
      I have run into what appears to be a DPL bug, where it seems to not be properly examining a class heirarchy to find the declaring class of a key field during a NULLIFY action. When using ABORT or CASCADE, the code works properly.

      In my code, the following stack trace is thrown:

      java.lang.IllegalArgumentException: Key field object may not be null
           at com.sleepycat.persist.impl.RecordOutput.writeKeyObject(RecordOutput.java:101)
           at com.sleepycat.persist.impl.RawAccessor.writeField(RawAccessor.java:219)
           at com.sleepycat.persist.impl.RawAccessor.writeSecKeyFields(RawAccessor.java:114)
           at com.sleepycat.persist.impl.ComplexFormat.writeObject(ComplexFormat.java:479)
           at com.sleepycat.persist.impl.PersistEntityBinding.writeEntity(PersistEntityBinding.java:114)
           at com.sleepycat.persist.impl.PersistKeyCreator.nullifyForeignKey(PersistKeyCreator.java:139)
           at com.sleepycat.je.SecondaryDatabase.onForeignKeyDelete(SecondaryDatabase.java:783)
           at com.sleepycat.je.ForeignKeyTrigger.databaseUpdated(ForeignKeyTrigger.java:38)
           at com.sleepycat.je.Database.notifyTriggers(Database.java:1343)
           at com.sleepycat.je.Database.deleteInternal(Database.java:492)
           at com.sleepycat.je.Database.delete(Database.java:406)
           at com.sleepycat.persist.BasicIndex.delete(BasicIndex.java:130)
           at com.sleepycat.persist.PrimaryIndex.delete(PrimaryIndex.java:200)
           at com.sleepycat.persist.BasicIndex.delete(BasicIndex.java:121)
           at com.sleepycat.persist.PrimaryIndex.delete(PrimaryIndex.java:200)

      I attempt to replicate this in a trivial form, and the stack trace changed somewhat. However, the cause/effect appears to be the same. If the line of code below commented with "DPL Bug Workaround" is uncommented, the code works as expected; but when DPL needs to look up the type of the key in a parent class, a stack trace is thrown. The stack trace is slightly different, so perhaps this is a second bug, or perhaps just another effect of the same bug.

      ExampleEntity.java:

      import com.sleepycat.persist.model.Entity;
      import com.sleepycat.persist.model.PrimaryKey;

      @Entity
      public final class ExampleEntity {
      @PrimaryKey(sequence = "ExampleEntity")
      public long id;

      public ExampleEntity () {
      }
      }

      ExampleBase.java:

      import static com.sleepycat.persist.model.DeleteAction.NULLIFY;
      import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;

      import com.sleepycat.persist.model.Entity;
      import com.sleepycat.persist.model.PrimaryKey;
      import com.sleepycat.persist.model.SecondaryKey;

      @Entity
      public abstract class ExampleBase {
      @PrimaryKey(sequence = "ExampleBase")
      public long id;

      @SecondaryKey(relate = MANY_TO_ONE, relatedEntity = ExampleEntity.class, onRelatedEntityDelete = NULLIFY)
      public Long eeId;

      protected ExampleBase () {
      }
      }

      ExampleSub.java:

      import com.sleepycat.persist.model.Persistent;

      @Persistent
      public final class ExampleSub extends ExampleBase {
      // XXX - DPL Bug Workaround
      // public Long eeId;


      public ExampleSub () {
      }
      }

      Main.java:
      ...
      final PrimaryIndex<Long, ExampleEntity> eeById = store.getPrimaryIndex(Long.class, ExampleEntity.class);
      final PrimaryIndex<Long, ExampleBase> esById = store.getPrimaryIndex(Long.class, ExampleBase.class);
      ExampleEntity ee1 = new ExampleEntity();
      eeById.put(ee1);
      ExampleSub es1 = new ExampleSub();
      es1.eeId = ee1.id;
      esById.put(es1);
      eeById.delete(ee1.id);
      ...

      When run, the 'delete' throws:

      java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
           at java.util.ArrayList.RangeCheck(Unknown Source)
           at java.util.ArrayList.get(Unknown Source)
           at com.sleepycat.persist.impl.RawAccessor.setField(RawAccessor.java:198)
           at com.sleepycat.persist.impl.ComplexFormat.nullifySecKey(ComplexFormat.java:677)
           at com.sleepycat.persist.impl.PersistKeyCreator.nullifyForeignKey(PersistKeyCreator.java:132)
           at com.sleepycat.je.SecondaryDatabase.onForeignKeyDelete(SecondaryDatabase.java:783)
           at com.sleepycat.je.ForeignKeyTrigger.databaseUpdated(ForeignKeyTrigger.java:38)
           at com.sleepycat.je.Database.notifyTriggers(Database.java:1343)
           at com.sleepycat.je.Database.deleteInternal(Database.java:492)
           at com.sleepycat.je.Database.delete(Database.java:406)
           at com.sleepycat.persist.BasicIndex.delete(BasicIndex.java:130)
           at com.sleepycat.persist.PrimaryIndex.delete(PrimaryIndex.java:200)
           at com.sleepycat.persist.BasicIndex.delete(BasicIndex.java:121)
           at com.sleepycat.persist.PrimaryIndex.delete(PrimaryIndex.java:200)
        • 1. Re: Nullify of superclass field fails
          Charles Lamb
          I'll take a look at this.

          -cwl
          • 2. Re: Nullify of superclass field fails
            Charles Lamb
            Hello,

            The test case you sent does in fact show a bug in the DPL and I have a fix for that. Thank you for sending that to us. If you'd like the fix, please email me charles.lamb -at- where you'd expect.com.

            However, the first stack trace does not appear to be quite the same exception. Could you please confirm that in your own code (i.e. not the code that you sent me) that the secondary key of the base class is a java primitive class (e.g. a long) and not a java wrapper class (e.g. a Long)? I can make this exception appear in your code if I change ExampleBase.eeId from Long to long.

            Thanks.

            Charles Lamb
            • 3. Re: Nullify of superclass field fails
              user593875
              The type in my base class is the wrapper class (Long), and so should be nullable.

              I was hoping that the one issue would appear while looking at the other. I'll re-attempt to make a test case that shows the right stack trace.
              • 4. Re: Nullify of superclass field fails
                Charles Lamb
                The fix is a one line fix. Look in com.sleepycat.persist.impl.RawAccessor.setField(). Add a return statement right after the call to superAccessor.setField() (inside the conditional). That should fix the bug in your example. If that fixes the problem in your own code then that will help us diagnose the problem.

                I can think of two other possibilities. If your secondary key is composite, you might see this exception. Is that possible or is it just a Long?

                Also, if you have an existing database and you didn't start from scratch, I'm thinking that it might be that you have a long in the schema (as opposed to a Long). Does this happen when you start from scratch with no existing Environment/EntityStore?

                Regards,

                Charles Lamb
                • 5. Re: Nullify of superclass field fails
                  user593875
                  Yes, that fixed the problem in my other code.

                  The key is not composite, just a Long.

                  My testing code ensures all the environment files are deleted before it opens the environment.
                  • 6. Re: Nullify of superclass field fails
                    692083
                    Version 3.3.75 still has this bug

                    When I delete a Parent instance, I want to keep my associated Child entities. I get the error in that post.

                    Here's the code:

                    @Entity
                    public class Parent {
                         
                         @PrimaryKey(sequence="ID")
                         long id;
                    ....

                    @Entity
                    public class Child implements Comparable<Child> {

                         @PrimaryKey(sequence="ID")
                         long id;
                         
                         @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Workout.class, onRelatedEntityDelete=DeleteAction.NULLIFY)
                         public long parentId;

                    ...


                    - Erick
                    • 7. Re: Nullify of superclass field fails
                      Greybird-Oracle
                      Could you please post a complete stack trace?
                      Thanks,
                      --mark                                                                                                                                                                                                   
                      • 8. Re: Nullify of superclass field fails
                        Greybird-Oracle
                        Did you really mean to use relatedEntity=Workout.class, or should it be relatedEntity=Parent.class?

                        --mark                                                                                                                                                                                                                           
                        • 9. Re: Nullify of superclass field fails
                          Greybird-Oracle
                          The parentId field must be type 'Long', not 'long', in order for it to be set to null (nullified).

                          An error message should be output by JE when getPrimaryIndex is called -- I'll file a bug for that.

                          --mark