From 1a4f2a93264b9faae00b4cc8541813b187f6b448 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Fri, 25 Jul 2008 06:13:20 +0000 Subject: Complete BigDecimal support -- ensure that failed update rolls back normalization. --- .../com/amazon/carbonado/gen/MasterFeature.java | 20 +-- .../carbonado/gen/MasterStorableGenerator.java | 166 +++++++++++++++------ 2 files changed, 127 insertions(+), 59 deletions(-) diff --git a/src/main/java/com/amazon/carbonado/gen/MasterFeature.java b/src/main/java/com/amazon/carbonado/gen/MasterFeature.java index f433ce6..fa0a06c 100644 --- a/src/main/java/com/amazon/carbonado/gen/MasterFeature.java +++ b/src/main/java/com/amazon/carbonado/gen/MasterFeature.java @@ -25,22 +25,22 @@ package com.amazon.carbonado.gen; * @since 1.2 */ public enum MasterFeature { - /** Insert and update operations implement record versioning, if version property exists */ + /** Insert and update operations implement record versioning, if version property exists. */ VERSIONING, - /** Insert and update operations normalize property types such as BigDecimal */ + /** Insert and update operations normalize property types such as BigDecimal. */ NORMALIZE, - /** Update operations load clean copy first, to prevent destructive update */ + /** Update operations load clean copy first, to prevent destructive update. */ UPDATE_FULL, - /** Ensure update operation always is in a transaction */ + /** Ensure update operation always is in a transaction. */ UPDATE_TXN, - /** Ensure update operation always is in a transaction, "for update" */ + /** Ensure update operation always is in a transaction, "for update". */ UPDATE_TXN_FOR_UPDATE, - /** Insert operation applies any sequences to unset properties */ + /** Insert operation applies any sequences to unset properties. */ INSERT_SEQUENCES, /** @@ -49,15 +49,15 @@ public enum MasterFeature { */ INSERT_CHECK_REQUIRED, - /** Ensure insert operation always is in a transaction */ + /** Ensure insert operation always is in a transaction. */ INSERT_TXN, - /** Ensure insert operation always is in a transaction, "for update" */ + /** Ensure insert operation always is in a transaction, "for update". */ INSERT_TXN_FOR_UPDATE, - /** Ensure delete operation always is in a transaction */ + /** Ensure delete operation always is in a transaction. */ DELETE_TXN, - /** Ensure delete operation always is in a transaction, "for update" */ + /** Ensure delete operation always is in a transaction, "for update". */ DELETE_TXN_FOR_UPDATE, } diff --git a/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java index 092eea8..82d8c7a 100644 --- a/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java @@ -130,20 +130,55 @@ public final class MasterStorableGenerator { { StorableInfo info = StorableIntrospector.examine(type); - anySequences: - if (features.contains(MasterFeature.INSERT_SEQUENCES)) { - for (StorableProperty property : info.getAllProperties().values()) { - if (!property.isDerived() && property.getSequenceName() != null) { - break anySequences; + if (features == null) { + features = EnumSet.noneOf(MasterFeature.class); + } else { + features = features.clone(); + } + + // Remove any features which don't apply. + { + boolean anySequences = false; + boolean doNormalize = false; + + if (features.contains(MasterFeature.INSERT_SEQUENCES) || + features.contains(MasterFeature.NORMALIZE)) + { + for (StorableProperty property : info.getAllProperties().values()) { + if (property.isDerived() || property.isJoin()) { + continue; + } + if (!anySequences) { + if (property.getSequenceName() != null) { + anySequences = true; + } + } + if (!doNormalize) { + if (BigDecimal.class.isAssignableFrom(property.getType())) { + doNormalize = true; + } + } + if (anySequences && doNormalize) { + break; + } + } + + if (!anySequences) { + features.remove(MasterFeature.INSERT_SEQUENCES); + } + + if (!doNormalize) { + features.remove(MasterFeature.NORMALIZE); } } - features.remove(MasterFeature.INSERT_SEQUENCES); - } - if (info.getVersionProperty() == null) { - features.remove(MasterFeature.VERSIONING); + if (info.getVersionProperty() == null) { + features.remove(MasterFeature.VERSIONING); + } } + // Add implied features. + if (features.contains(MasterFeature.VERSIONING)) { // Implied feature. features.add(MasterFeature.UPDATE_FULL); @@ -495,16 +530,11 @@ public final class MasterStorableGenerator { } // Copy of properties before normalization. - List unnormalized = null; - if (mFeatures.contains(MasterFeature.NORMALIZE)) { - unnormalized = addNormalization(b, false); - } - + List unnormalized = addNormalization(b, false); Label doTryStart = b.createLabel().setLocation(); b.loadThis(); b.invokeVirtual(DO_TRY_INSERT_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - addNormalizationRollback(b, doTryStart, unnormalized); if (tryStart == null) { @@ -555,20 +585,17 @@ public final class MasterStorableGenerator { Label tryLoadStart = null, tryLoadEnd = null; - // Copy of properties before normalization. - List unnormalized = null; - if (mFeatures.contains(MasterFeature.NORMALIZE)) { - unnormalized = addNormalization(b, false); - } - - Label doTryStart = b.createLabel().setLocation(); - if (!mFeatures.contains(MasterFeature.UPDATE_FULL)) { + // Copy of properties before normalization. + List unnormalized = addNormalization(b, true); + Label doTryStart = b.createLabel().setLocation(); + // if (!this.doTryUpdateMaster()) { // goto failed; // } b.loadThis(); b.invokeVirtual(DO_TRY_UPDATE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); + addNormalizationRollback(b, doTryStart, unnormalized); b.ifZeroComparisonBranch(failed, "=="); } else { // Storable saved = copy(); @@ -707,6 +734,10 @@ public final class MasterStorableGenerator { allowedVersion.setLocation(); } + // Copy of properties before normalization. + List unnormalized = addNormalization(b, true); + Label doTryStart = b.createLabel().setLocation(); + // this.copyDirtyProperties(saved); // if (version support enabled) { // saved.setVersionNumber(saved.getVersionNumber() + 1); @@ -725,6 +756,7 @@ public final class MasterStorableGenerator { // } b.loadLocal(savedVar); b.invokeVirtual(DO_TRY_UPDATE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); + addNormalizationRollback(b, doTryStart, unnormalized); b.ifZeroComparisonBranch(failed, "=="); // saved.copyUnequalProperties(this); @@ -1051,8 +1083,12 @@ public final class MasterStorableGenerator { private List addNormalization(CodeBuilder b, boolean forUpdate) { List unnormalized = null; + if (!mFeatures.contains(MasterFeature.NORMALIZE)) { + return unnormalized; + } + for (StorableProperty property : mAllProperties.values()) { - if (property.isDerived()) { + if (property.isDerived() || property.isJoin()) { continue; } if (!BigDecimal.class.isAssignableFrom(property.getType())) { @@ -1068,20 +1104,50 @@ public final class MasterStorableGenerator { copy.makeCopy(b); + // Skip property if null. b.loadLocal(copy.copyVar); Label skipNormalize = b.createLabel(); b.ifNullBranch(skipNormalize, true); if (forUpdate) { - // FIXME: for update, also check if dirty + // Also skip property if not dirty. + + String stateFieldName = + StorableGenerator.PROPERTY_STATE_FIELD_NAME + (property.getNumber() >> 4); + + b.loadThis(); + b.loadField(stateFieldName, TypeDesc.INT); + int shift = (property.getNumber() & 0xf) * 2; + b.loadConstant(StorableGenerator.PROPERTY_STATE_MASK << shift); + b.math(Opcode.IAND); + b.loadConstant(StorableGenerator.PROPERTY_STATE_DIRTY << shift); + + b.ifComparisonBranch(skipNormalize, "!="); } - // Normalize by stripping trailing zeros. - // FIXME: Workaround BigDecimal.ZERO bug. + // Normalize by stripping trailing zeros and also workaround + // BigDecimal.ZERO bug when calling stripTrailingZeros. #6480539 + + // Load this in preparation for storing to field. b.loadThis(); - b.loadLocal(copy.copyVar); + TypeDesc propertyType = copy.copyVar.getType(); + + b.loadStaticField(propertyType, "ZERO", propertyType); + b.loadLocal(copy.copyVar); + b.invokeVirtual(propertyType, "compareTo", TypeDesc.INT, + new TypeDesc[] {propertyType}); + Label notZero = b.createLabel(); + b.ifZeroComparisonBranch(notZero, "!="); + b.loadStaticField(propertyType, "ZERO", propertyType); + Label storeField = b.createLabel(); + b.branch(storeField); + + notZero.setLocation(); + b.loadLocal(copy.copyVar); b.invokeVirtual(propertyType, "stripTrailingZeros", propertyType, null); + + storeField.setLocation(); b.storeField(property.getName(), propertyType); skipNormalize.setLocation(); @@ -1096,31 +1162,33 @@ public final class MasterStorableGenerator { private void addNormalizationRollback(CodeBuilder b, Label doTryStart, List unnormalized) { - if (unnormalized != null) { - Label doTryEnd = b.createLabel().setLocation(); + if (unnormalized == null) { + return; + } - b.dup(); - Label success = b.createLabel(); - b.ifZeroComparisonBranch(success, "!="); + Label doTryEnd = b.createLabel().setLocation(); - for (int i=0; i<2; i++) { - if (i == 0) { - } else { - b.exceptionHandler(doTryStart, doTryEnd, null); - } - // Rollback normalized properties. - for (PropertyCopy copy : unnormalized) { - copy.restore(b); - } - if (i == 0) { - b.branch(success); - } else { - b.throwObject(); - } - } + b.dup(); + Label success = b.createLabel(); + b.ifZeroComparisonBranch(success, "!="); - success.setLocation(); + for (int i=0; i<2; i++) { + if (i == 0) { + } else { + b.exceptionHandler(doTryStart, doTryEnd, null); + } + // Rollback normalized properties. + for (PropertyCopy copy : unnormalized) { + copy.restore(b); + } + if (i == 0) { + b.branch(success); + } else { + b.throwObject(); + } } + + success.setLocation(); } private static class PropertyCopy { -- cgit v1.2.3