From 1d9e9f45ffe83515b2a6da1eb6ad320d46f9ce9f Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Wed, 10 Mar 2010 01:25:48 +0000 Subject: Add master feature to check if partition key has been fully specified. --- .../com/amazon/carbonado/gen/MasterFeature.java | 3 + .../carbonado/gen/MasterStorableGenerator.java | 289 +++++++++++++++------ .../amazon/carbonado/gen/StorableGenerator.java | 33 ++- 3 files changed, 243 insertions(+), 82 deletions(-) (limited to 'src/main/java/com') diff --git a/src/main/java/com/amazon/carbonado/gen/MasterFeature.java b/src/main/java/com/amazon/carbonado/gen/MasterFeature.java index fa0a06c..a636b9c 100644 --- a/src/main/java/com/amazon/carbonado/gen/MasterFeature.java +++ b/src/main/java/com/amazon/carbonado/gen/MasterFeature.java @@ -60,4 +60,7 @@ public enum MasterFeature { /** Ensure delete operation always is in a transaction, "for update". */ DELETE_TXN_FOR_UPDATE, + + /** Enforce rules for Storables which have a partition key */ + PARTITIONING } diff --git a/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java index afb1a29..1c63d0c 100644 --- a/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Copyright 2006-2010 Amazon Technologies, Inc. or its affiliates. * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks * of Amazon Technologies, Inc. or its affiliates. All rights reserved. * @@ -64,6 +64,7 @@ import static com.amazon.carbonado.gen.CommonMethodNames.*; * transactions since this class takes care of that. * * @author Brian S O'Neill + * @author Olga Kuznetsova * @since 1.2 */ public final class MasterStorableGenerator { @@ -175,6 +176,10 @@ public final class MasterStorableGenerator { if (info.getVersionProperty() == null) { features.remove(MasterFeature.VERSIONING); } + + if (info.getPartitionKey() == null || info.getPartitionKey().getProperties().size() == 0) { + features.remove(MasterFeature.PARTITIONING); + } } // Add implied features. @@ -293,99 +298,151 @@ public final class MasterStorableGenerator { mi.addException(persistExceptionType); } - // Add required protected doTryInsert method. + // Add features pertaining to partitioning { - // If sequence support requested, implement special insert hook to - // call sequences for properties which are UNINITIALIZED. User may - // provide explicit values for properties with sequences. + if (mFeatures.contains(MasterFeature.PARTITIONING)) { - if (mFeatures.contains(MasterFeature.INSERT_SEQUENCES)) { - MethodInfo mi = mClassFile.addMethod - (Modifiers.PROTECTED, - StorableGenerator.CHECK_PK_FOR_INSERT_METHOD_NAME, - null, null); - CodeBuilder b = new CodeBuilder(mi); - - int ordinal = 0; + // Overwrite write function for each property if Partitioning is enabled to check + // that properties that are part of the Partition key are not overwritten when clean for (StorableProperty property : mAllProperties.values()) { - if (!property.isDerived() && property.getSequenceName() != null) { - // Check the state of this property, to see if it is - // uninitialized. Uninitialized state has value zero. - + if (!property.isDerived() && property.isPartitionKeyMember()) { + Method writeMethod = property.getWriteMethod(); + MethodInfo mi; + String writeName = property.getWriteMethodName(); + final TypeDesc type = TypeDesc.forClass(property.getType()); + if (writeMethod != null) { + mi = mClassFile.addMethod(writeMethod); + } else { + continue; + } + CodeBuilder b = new CodeBuilder(mi); String stateFieldName = - StorableGenerator.PROPERTY_STATE_FIELD_NAME + (ordinal >> 4); - + StorableGenerator.PROPERTY_STATE_FIELD_NAME + (property.getNumber() >> 4); b.loadThis(); b.loadField(stateFieldName, TypeDesc.INT); - int shift = (ordinal & 0xf) * 2; - b.loadConstant(StorableGenerator.PROPERTY_STATE_MASK << shift); + b.loadConstant(StorableGenerator.PROPERTY_STATE_MASK << ((property.getNumber() & 0xf) * 2)); b.math(Opcode.IAND); - - Label isInitialized = b.createLabel(); - b.ifZeroComparisonBranch(isInitialized, "!="); - - // Load this in preparation for storing value to property. + b.loadConstant(StorableGenerator.PROPERTY_STATE_CLEAN << ((property.getNumber() & 0xf) * 2)); + Label isMutable = b.createLabel(); + b.ifComparisonBranch(isMutable, "!="); + CodeBuilderUtil.throwException + (b, IllegalStateException.class, "Cannot alter partition key"); + isMutable.setLocation(); b.loadThis(); + b.loadLocal(b.getParameter(0)); + b.invokeSuper(mClassFile.getSuperClassName(), + writeName, + null, new TypeDesc[]{type}); + b.returnVoid(); + } + } + } + } + + // Add required protected doTryInsert method. + { + if (mFeatures.contains(MasterFeature.PARTITIONING) || + mFeatures.contains(MasterFeature.INSERT_SEQUENCES)) { - // Call MasterSupport.getSequenceValueProducer(String). - TypeDesc seqValueProdType = TypeDesc.forClass(SequenceValueProducer.class); - b.loadThis(); - b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType); - b.checkCast(masterSupportType); - b.loadConstant(property.getSequenceName()); - b.invokeInterface - (masterSupportType, "getSequenceValueProducer", - seqValueProdType, new TypeDesc[] {TypeDesc.STRING}); - - // Find appropriate method to call for getting next sequence value. - TypeDesc propertyType = TypeDesc.forClass(property.getType()); - TypeDesc propertyObjType = propertyType.toObjectType(); - Method method; - - try { - if (propertyObjType == TypeDesc.LONG.toObjectType()) { - method = SequenceValueProducer.class - .getMethod("nextLongValue", (Class[]) null); - } else if (propertyObjType == TypeDesc.INT.toObjectType()) { - method = SequenceValueProducer.class - .getMethod("nextIntValue", (Class[]) null); - } else if (propertyObjType == TypeDesc.STRING) { - method = SequenceValueProducer.class - .getMethod("nextDecimalValue", (Class[]) null); - } else { - throw new SupportException - ("Unable to support sequence of type \"" + - TypeDesc.forClass(property.getType()).getFullName() + - "\" for property: " + property.getName()); + MethodInfo mi = mClassFile.addMethod + (Modifiers.PROTECTED, + StorableGenerator.CHECK_PK_FOR_INSERT_METHOD_NAME, + null, null); + CodeBuilder b = new CodeBuilder(mi); + + if (mFeatures.contains(MasterFeature.PARTITIONING)) { + // Check that partition key exists in insert operation + checkIfPartitionKeyPresent(b); + } + + // If sequence support requested, implement special insert hook to + // call sequences for properties which are UNINITIALIZED. User may + // provide explicit values for properties with sequences. + + if (mFeatures.contains(MasterFeature.INSERT_SEQUENCES)) { + + int ordinal = 0; + for (StorableProperty property : mAllProperties.values()) { + if (!property.isDerived() && property.getSequenceName() != null) { + // Check the state of this property, to see if it is + // uninitialized. Uninitialized state has value zero. + + String stateFieldName = + StorableGenerator.PROPERTY_STATE_FIELD_NAME + (ordinal >> 4); + + b.loadThis(); + b.loadField(stateFieldName, TypeDesc.INT); + int shift = (ordinal & 0xf) * 2; + b.loadConstant(StorableGenerator.PROPERTY_STATE_MASK << shift); + b.math(Opcode.IAND); + + Label isInitialized = b.createLabel(); + b.ifZeroComparisonBranch(isInitialized, "!="); + + // Load this in preparation for storing value to property. + b.loadThis(); + + // Call MasterSupport.getSequenceValueProducer(String). + TypeDesc seqValueProdType = TypeDesc.forClass(SequenceValueProducer.class); + b.loadThis(); + b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType); + b.checkCast(masterSupportType); + b.loadConstant(property.getSequenceName()); + b.invokeInterface + (masterSupportType, "getSequenceValueProducer", + seqValueProdType, new TypeDesc[] {TypeDesc.STRING}); + + // Find appropriate method to call for getting next sequence value. + TypeDesc propertyType = TypeDesc.forClass(property.getType()); + TypeDesc propertyObjType = propertyType.toObjectType(); + Method method; + + try { + if (propertyObjType == TypeDesc.LONG.toObjectType()) { + method = SequenceValueProducer.class + .getMethod("nextLongValue", (Class[]) null); + } else if (propertyObjType == TypeDesc.INT.toObjectType()) { + method = SequenceValueProducer.class + .getMethod("nextIntValue", (Class[]) null); + } else if (propertyObjType == TypeDesc.STRING) { + method = SequenceValueProducer.class + .getMethod("nextDecimalValue", (Class[]) null); + } else { + throw new SupportException + ("Unable to support sequence of type \"" + + TypeDesc.forClass(property.getType()).getFullName() + + "\" for property: " + property.getName()); + } + } catch (NoSuchMethodException e) { + Error err = new NoSuchMethodError(); + err.initCause(e); + throw err; } - } catch (NoSuchMethodException e) { - Error err = new NoSuchMethodError(); - err.initCause(e); - throw err; + + b.invoke(method); + b.convert(TypeDesc.forClass(method.getReturnType()), propertyType); + + // Store property + b.storeField(property.getName(), propertyType); + + // Set state to dirty. + b.loadThis(); + b.loadThis(); + b.loadField(stateFieldName, TypeDesc.INT); + b.loadConstant(StorableGenerator.PROPERTY_STATE_DIRTY << shift); + b.math(Opcode.IOR); + b.storeField(stateFieldName, TypeDesc.INT); + + isInitialized.setLocation(); } - - b.invoke(method); - b.convert(TypeDesc.forClass(method.getReturnType()), propertyType); - - // Store property - b.storeField(property.getName(), propertyType); - - // Set state to dirty. - b.loadThis(); - b.loadThis(); - b.loadField(stateFieldName, TypeDesc.INT); - b.loadConstant(StorableGenerator.PROPERTY_STATE_DIRTY << shift); - b.math(Opcode.IOR); - b.storeField(stateFieldName, TypeDesc.INT); - - isInitialized.setLocation(); + + ordinal++; } - - ordinal++; + + // We've tried our best to fill in missing values, now run the + // original check method. } - // We've tried our best to fill in missing values, now run the - // original check method. b.loadThis(); b.invokeSuper(mClassFile.getSuperClassName(), StorableGenerator.CHECK_PK_FOR_INSERT_METHOD_NAME, @@ -579,9 +636,27 @@ public final class MasterStorableGenerator { addExitTransaction(b, INSERT_OP, txnVar, tryStart); } } - + // Add required protected doTryUpdate method. addDoTryUpdate: { + if (mFeatures.contains(MasterFeature.PARTITIONING)) { + // Check that partition key exists in update operation + + MethodInfo mi = mClassFile.addMethod + (Modifiers.PROTECTED, + StorableGenerator.CHECK_PK_FOR_UPDATE_METHOD_NAME, + null, null); + CodeBuilder b = new CodeBuilder(mi); + + checkIfPartitionKeyPresent(b); + + b.loadThis(); + b.invokeSuper(mClassFile.getSuperClassName(), + StorableGenerator.CHECK_PK_FOR_UPDATE_METHOD_NAME, + null, null); + b.returnVoid(); + } + MethodInfo mi = mClassFile.addMethod (Modifiers.PROTECTED.toFinal(true), StorableGenerator.DO_TRY_UPDATE_METHOD_NAME, TypeDesc.BOOLEAN, null); @@ -818,6 +893,23 @@ public final class MasterStorableGenerator { // Add required protected doTryDelete method. { + if (mFeatures.contains(MasterFeature.PARTITIONING)) { + // Check that partition key exists in delete operation + MethodInfo mi = mClassFile.addMethod + (Modifiers.PROTECTED, + StorableGenerator.CHECK_PK_FOR_DELETE_METHOD_NAME, + null, null); + CodeBuilder b = new CodeBuilder(mi); + + checkIfPartitionKeyPresent(b); + + b.loadThis(); + b.invokeSuper(mClassFile.getSuperClassName(), + StorableGenerator.CHECK_PK_FOR_DELETE_METHOD_NAME, + null, null); + b.returnVoid(); + } + MethodInfo mi = mClassFile.addMethod (Modifiers.PROTECTED.toFinal(true), StorableGenerator.DO_TRY_DELETE_METHOD_NAME, TypeDesc.BOOLEAN, null); @@ -848,6 +940,25 @@ public final class MasterStorableGenerator { addExitTransaction(b, DELETE_OP, txnVar, tryStart); } } + + // Check that partition key exists for Load + { + if (mFeatures.contains(MasterFeature.PARTITIONING)) { + MethodInfo mi = mClassFile.addMethod + (Modifiers.PROTECTED, + StorableGenerator.CHECK_PK_FOR_LOAD_METHOD_NAME, + null, null); + CodeBuilder b = new CodeBuilder(mi); + + checkIfPartitionKeyPresent(b); + + b.loadThis(); + b.invokeSuper(mClassFile.getSuperClassName(), + StorableGenerator.CHECK_PK_FOR_LOAD_METHOD_NAME, + null, null); + b.returnVoid(); + } + } } private void branchIfNull(CodeBuilder b, LocalVariable value, Label isNull) { @@ -1241,6 +1352,22 @@ public final class MasterStorableGenerator { success.setLocation(); } + private void checkIfPartitionKeyPresent(CodeBuilder b) { + b.loadThis(); + b.invokeVirtual(StorableGenerator.IS_PARTITION_KEY_INITIALIZED_METHOD_NAME, TypeDesc.BOOLEAN, null); + Label ptnkInitialized = b.createLabel(); + b.ifZeroComparisonBranch(ptnkInitialized, "!="); + + TypeDesc exType = TypeDesc.forClass(IllegalStateException.class); + b.newObject(exType); + b.dup(); + b.loadConstant("Partition key not fully specified"); + b.invokeConstructor(exType, new TypeDesc[] {TypeDesc.STRING}); + b.throwObject(); + + ptnkInitialized.setLocation(); + } + private static class PropertyCopy { final StorableProperty property; final LocalVariable copyVar; diff --git a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java index 1cf4889..0aeb541 100644 --- a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java @@ -109,7 +109,8 @@ public final class StorableGenerator { public static final String CHECK_PK_FOR_INSERT_METHOD_NAME = "checkPkForInsert$", CHECK_PK_FOR_UPDATE_METHOD_NAME = "checkPkForUpdate$", - CHECK_PK_FOR_DELETE_METHOD_NAME = "checkPkForDelete$"; + CHECK_PK_FOR_DELETE_METHOD_NAME = "checkPkForDelete$", + CHECK_PK_FOR_LOAD_METHOD_NAME = "checkPkForLoad$"; /** * Name of protected method in generated storable that returns false if any @@ -117,6 +118,12 @@ public final class StorableGenerator { */ public static final String IS_PK_INITIALIZED_METHOD_NAME = "isPkInitialized$"; + /** + * Name of protected method in generated storable that returns false if any + * partition keys are uninitialized. + */ + public static final String IS_PARTITION_KEY_INITIALIZED_METHOD_NAME = "isPartitionKeyInitialized$"; + /** * Name prefix of protected method in generated storable that returns false * if a specific alternate key is uninitialized. The complete name is @@ -279,6 +286,9 @@ public final class StorableGenerator { * // Returns true if all primary key properties have been set. * protected boolean isPkInitialized(); * + * // Returns true if all partition key properties have been set. + * protected boolean isPartitionKeyInitialized(); + * * // Returns true if all required data properties are set. * // A required data property is a non-nullable, non-primary key. * protected boolean isRequiredDataInitialized(); @@ -1011,6 +1021,13 @@ public final class StorableGenerator { CodeBuilder b = new CodeBuilder(mi); + // Add empty method that will be overrriden if there is a partition key to check that it is initialized + b.loadThis(); + b.invokeVirtual(CHECK_PK_FOR_LOAD_METHOD_NAME, null, null); + CodeBuilder b1 = new CodeBuilder(mClassFile.addMethod(Modifiers.PROTECTED, CHECK_PK_FOR_LOAD_METHOD_NAME, null, null)); + b1.loadThis(); + b1.returnVoid(); + // Check that primary key is initialized. b.loadThis(); b.invokeVirtual(IS_PK_INITIALIZED_METHOD_NAME, TypeDesc.BOOLEAN, null); @@ -1697,6 +1714,20 @@ public final class StorableGenerator { addIsInitializedMethod (IS_PK_INITIALIZED_METHOD_NAME, mInfo.getPrimaryKeyProperties()); + { + // Define protected isPartitionKeyInitialized method + // It will return true if there are no Partition keys + final Map> partitionProperties = new LinkedHashMap>(); + for (StorableProperty property : mAllProperties.values()) { + if (!property.isDerived() && property.isPartitionKeyMember()) { + partitionProperties.put(property.getName(), property); + } + } + + // Add methods to check that the partition key is defined + addIsInitializedMethod(IS_PARTITION_KEY_INITIALIZED_METHOD_NAME, partitionProperties); + } + // Define protected methods to check if alternate key is initialized. addAltKeyMethods: for (int i=0; i