db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhille...@apache.org
Subject svn commit: r907654 [1/2] - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/reference/ engine/org/apache/derby/iapi/sql/dictionary/ engine/org/apache/derby/impl/sql/catalog/ engine/org/apache/derby/impl/sql/compile/ engine/org/apache/derby/i...
Date Mon, 08 Feb 2010 13:51:37 GMT
Author: rhillegas
Date: Mon Feb  8 13:51:35 2010
New Revision: 907654

URL: http://svn.apache.org/viewvc?rev=907654&view=rev
Log:
DERBY-712: Wire in cacheable sequence generation, making the NEXT VALUE FOR clause behave as expected.

Added:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceGenerator.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SGVetter.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SequenceGeneratorTest.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDescriptorGenerator.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/SequenceDescriptor.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SYSSEQUENCESRowFactory.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NextSequenceNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateSequenceConstantAction.java
    db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
    db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
    db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GeneratedColumnsHelper.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SequenceTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UDTTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/Property.java Mon Feb  8 13:51:35 2010
@@ -598,6 +598,15 @@
 	int		LANG_SPS_CACHE_SIZE_DEFAULT =32;
 
 	/**
+	 * The size of the sequence generator cache 
+	 * used by the data dictionary.  Database.  Static.
+	 * <p>
+	 * Externally visible.
+	 */
+	String	LANG_SEQGEN_CACHE_SIZE = "derby.language.sequenceGeneratorCacheSize";
+	int		LANG_SEQGEN_CACHE_SIZE_DEFAULT =32;
+
+	/**
 	  derby.language.stalePlanCheckInterval
 
 	  <P>

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDescriptorGenerator.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDescriptorGenerator.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDescriptorGenerator.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDescriptorGenerator.java Mon Feb  8 13:51:35 2010
@@ -528,7 +528,7 @@
             UUID uuid,
             String sequenceName,
             DataTypeDescriptor dataType,
-            long currentValue,
+            Long currentValue,
             long startValue,
             long minimumValue,
             long maximumValue,

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java Mon Feb  8 13:51:35 2010
@@ -1629,6 +1629,24 @@
 		throws StandardException;
 	
 	/**
+	 * Get the next number from an ANSI/ISO sequence generator
+     * which was created with the CREATE SEQUENCE statement. May
+     * raise an exception if the sequence was defined as NO CYCLE and
+     * the range of the sequence is exhausted. May allocate a range of
+     * sequence numbers and update the CURRENTVALUE column of the
+     * corresponding row in SYSSEQUENCES. This work is done in the
+     * execution transaction of the current session.
+	 * 
+	 * @param sequenceUUIDstring String value of the UUID which identifies the sequence
+	 * @param returnValue This is a data value to be stuffed with the next sequence number.
+     *
+     * @throws StandardException if the sequence does not cycle and its range is exhausted
+	 */
+    public void getCurrentValueAndAdvance
+        ( String sequenceUUIDstring, NumberDataValue returnValue )
+        throws StandardException;
+
+	/**
 	 * Gets all statistics Descriptors for a given table.
 	 */
 	public List getStatisticsDescriptors(TableDescriptor td)

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/SequenceDescriptor.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/SequenceDescriptor.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/SequenceDescriptor.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/SequenceDescriptor.java Mon Feb  8 13:51:35 2010
@@ -37,6 +37,10 @@
 
 /**
  * This class is used by rows in the SYS.SYSSEQUENCES system table.
+ * See the header comment of SYSSEQUENCESRowFactory for the
+ * contract of that table. In particular, if the CURRENTVALUE column
+ * is null, then the sequence has been exhausted and no more values
+ * can be generated from it.
  */
 public class SequenceDescriptor extends TupleDescriptor
         implements Provider, Dependent, PrivilegedSQLObject
@@ -47,12 +51,12 @@
     private final SchemaDescriptor schemaDescriptor;
     private UUID schemaId;
     private DataTypeDescriptor dataType;
-    private long currentValue;
+    private Long currentValue; // could be null
     private long startValue;
     private long minimumValue;
     private long maximumValue;
     private long increment;
-    private boolean cycle;
+    private boolean canCycle;
 
     /**
      * Constructor
@@ -64,8 +68,8 @@
      */
 
     public SequenceDescriptor(DataDictionary dataDictionary, SchemaDescriptor sd, UUID sequenceUUID, String sequenceName,
-                              DataTypeDescriptor dataType, long currentValue,
-                              long startValue, long minimumValue, long maximumValue, long increment, boolean cycle) {
+                              DataTypeDescriptor dataType, Long currentValue,
+                              long startValue, long minimumValue, long maximumValue, long increment, boolean canCycle) {
         super(dataDictionary);
         if (SanityManager.DEBUG) {
             if (sd.getSchemaName() == null) {
@@ -83,7 +87,7 @@
         this.minimumValue = minimumValue;
         this.maximumValue = maximumValue;
         this.increment = increment;
-        this.cycle = cycle;
+        this.canCycle = canCycle;
     }
 
    /**
@@ -113,7 +117,7 @@
                     "minimumValue: " + minimumValue + "\n" +
                     "maximumValue: " + maximumValue + "\n" +
                     "increment: " + increment + "\n" +
-                    "cycle: " + cycle + "\n";
+                    "canCycle: " + canCycle + "\n";
         } else {
             return "";
         }
@@ -284,7 +288,7 @@
         return dataType;
     }
 
-    public long getCurrentValue() {
+    public Long getCurrentValue() {
         return currentValue;
     }
 
@@ -304,7 +308,7 @@
         return increment;
     }
 
-    public boolean isCycle() {
-        return cycle;
+    public boolean canCycle() {
+        return canCycle;
     }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java Mon Feb  8 13:51:35 2010
@@ -369,10 +369,12 @@
 	CacheManager	OIDTdCache;
 	CacheManager	nameTdCache;
 	private CacheManager	spsNameCache;
+    private CacheManager sequenceGeneratorCache;
 	private Hashtable		spsIdHash;
 	// private Hashtable       spsTextHash;
 	int				tdCacheSize;
-	int				stmtCacheSize;	
+	int				stmtCacheSize;
+    private int seqgenCacheSize;
 
     /* Cache of permissions data */
     CacheManager permissionsCache;
@@ -602,6 +604,10 @@
 		stmtCacheSize = PropertyUtil.intPropertyValue(Property.LANG_SPS_CACHE_SIZE, value,
 									   0, Integer.MAX_VALUE, Property.LANG_SPS_CACHE_SIZE_DEFAULT);
 
+		value = startParams.getProperty(Property.LANG_SEQGEN_CACHE_SIZE);
+		seqgenCacheSize = PropertyUtil.intPropertyValue(Property.LANG_SEQGEN_CACHE_SIZE, value,
+									   0, Integer.MAX_VALUE, Property.LANG_SEQGEN_CACHE_SIZE_DEFAULT);
+
 		value = startParams.getProperty(Property.LANG_PERMISSIONS_CACHE_SIZE);
 		permissionsCacheSize = PropertyUtil.intPropertyValue(Property.LANG_PERMISSIONS_CACHE_SIZE, value,
 									   0, Integer.MAX_VALUE, Property.LANG_PERMISSIONS_CACHE_SIZE_DEFAULT);
@@ -640,6 +646,8 @@
 			// spsTextHash = new Hashtable(stmtCacheSize);
 		}
 
+		sequenceGeneratorCache = cf.newCacheManager
+            ( this, "SequenceGeneratorCache", seqgenCacheSize, seqgenCacheSize );
 
 		/* Get the object to coordinate cache transitions */
 		cacheCoordinator = new ShExLockable();
@@ -868,15 +876,11 @@
 	*/
 	public Cacheable newCacheable(CacheManager cm) {
 
-		if (cm == OIDTdCache)
-			return new OIDTDCacheable(this);
-		else if (cm == nameTdCache)
-			return new NameTDCacheable(this);
-        else if( cm == permissionsCache)
-            return new PermissionsCacheable(this);
-		else {
-			return new SPSNameCacheable(this);
-		}
+		if ( cm == OIDTdCache ) { return new OIDTDCacheable( this ); }
+		else if ( cm == nameTdCache ) { return new NameTDCacheable( this ); }
+        else if ( cm == permissionsCache ) { return new PermissionsCacheable( this ); }
+        else if ( cm == sequenceGeneratorCache ) { return new SequenceUpdater.SyssequenceUpdater( this ); }
+		else { return new SPSNameCacheable( this ); }
 	}
 
 	/*
@@ -8134,6 +8138,8 @@
 		nameTdCache.ageOut();
 		OIDTdCache.cleanAll();
 		OIDTdCache.ageOut();
+		sequenceGeneratorCache.cleanAll();
+		sequenceGeneratorCache.ageOut();
 		if (spsNameCache != null)
 		{
 			//System.out.println("CLEARING SPS CACHE");
@@ -9389,6 +9395,146 @@
 								 SYSCOLUMNSRowFactory.SYSCOLUMNS_INDEX1_ID);
 	}
 
+	/**
+	 * Computes the RowLocation in SYSSEQUENCES for a particular sequence. Also
+     * constructs the sequence descriptor.
+	 * 
+	 * @param tc			Transaction Controller to use.
+	 * @param sequenceIDstring UUID of the sequence as a string
+	 * @param rowLocation OUTPUT param for returing the row location
+	 * @param sequenceDescriptor OUTPUT param for return the sequence descriptor
+     *
+	 * @return The RowLocation of that sequence in SYSSEQUENCES
+	 * 
+	 * @exception StandardException thrown on failure.
+	 */ 
+	void computeSequenceRowLocation
+        ( TransactionController tc, String sequenceIDstring, RowLocation[] rowLocation, SequenceDescriptor[] sequenceDescriptor )
+		throws StandardException								  
+	{
+		TabInfoImpl ti = getNonCoreTI(SYSSEQUENCES_CATALOG_NUM);
+		ExecIndexRow keyRow = null;
+		ExecRow row;
+
+		keyRow = (ExecIndexRow)exFactory.getIndexableRow(1);
+		keyRow.setColumn(1, new SQLChar( sequenceIDstring ) );
+        
+		rowLocation[ 0 ] = ti.getRowLocation( tc, keyRow, SYSSEQUENCESRowFactory.SYSSEQUENCES_INDEX1_ID );
+        
+        sequenceDescriptor[ 0 ] = (SequenceDescriptor)
+            getDescriptorViaIndexMinion
+            (
+             SYSSEQUENCESRowFactory.SYSSEQUENCES_INDEX1_ID,
+             keyRow,
+             (ScanQualifier[][]) null,
+             ti,
+             (TupleDescriptor) null,
+             (List) null,
+             false,
+             TransactionController.ISOLATION_REPEATABLE_READ,
+             tc);
+	}
+
+	/**
+	 * Set the current value of an ANSI/ISO sequence. This method does not perform
+     * any sanity checking but assumes that the caller knows what they are doing. If the
+     * old value on disk is not what we expect it to be, then we are in a race with another
+     * session. They won and we don't update the value on disk. However, if the old value
+     * is null, that is a signal to us that we should update the value on disk anyway.
+	 * 
+	 * @param tc			Transaction Controller to use.
+	 * @param rowLocation Row in SYSSEQUENCES to update.
+     * @param wait True if we should wait for locks
+     * @param oldValue What we expect to find in the CURRENTVALUE column.
+     * @param newValue What to stuff into the CURRENTVALUE column.
+	 * 
+	 * @return Returns true if the value was successfully updated, false if we lost a race with another session.
+     *
+	 * @exception StandardException thrown on failure.
+	 */
+    boolean updateCurrentSequenceValue
+        ( TransactionController tc, RowLocation rowLocation, boolean wait, Long oldValue, Long newValue )
+        throws StandardException
+    {
+  		int columnNum = SYSSEQUENCESRowFactory.SYSSEQUENCES_CURRENT_VALUE;
+		FormatableBitSet columnToUpdate = new FormatableBitSet( SYSSEQUENCESRowFactory.SYSSEQUENCES_COLUMN_COUNT );
+		TabInfoImpl ti = getNonCoreTI( SYSSEQUENCES_CATALOG_NUM );
+  		ConglomerateController heapCC = null;
+		SYSSEQUENCESRowFactory	rf = (SYSSEQUENCESRowFactory) ti.getCatalogRowFactory();
+		ExecRow row = rf.makeEmptyRow();
+        
+		// FormatableBitSet is 0 based.
+        columnToUpdate.set( columnNum - 1 ); // current value.
+        
+        try
+        {
+			/* if wait is true then we need to do a wait while trying to
+			   open/fetch from the conglomerate. note we use wait both to
+			   open as well as fetch from the conglomerate.
+			*/
+            heapCC = 
+                tc.openConglomerate(
+                    ti.getHeapConglomerate(), 
+                    false,
+                    (TransactionController.OPENMODE_FORUPDATE |
+                     ((wait) ? 0 : TransactionController.OPENMODE_LOCK_NOWAIT)),
+                    TransactionController.MODE_RECORD,
+                    TransactionController.ISOLATION_REPEATABLE_READ);
+
+            heapCC.fetch( rowLocation, row.getRowArray(), columnToUpdate, wait );
+
+			NumberDataValue oldValueOnDisk = (NumberDataValue) row.getColumn( columnNum );
+
+            SQLLongint expectedOldValue;
+            if ( oldValue == null ) { expectedOldValue = new SQLLongint(); }
+            else { expectedOldValue = new SQLLongint( oldValue.longValue() ); }
+
+            // only update value if what's on disk is what we expected
+            if ( ( oldValue == null ) || ( expectedOldValue.compare( oldValueOnDisk ) == 0 ) )
+            {
+                SQLLongint newValueOnDisk;
+                if ( newValue == null ) { newValueOnDisk = new SQLLongint(); }
+                else { newValueOnDisk = new SQLLongint( newValue.longValue() ); }
+                
+                row.setColumn( columnNum, newValueOnDisk );
+                heapCC.replace( rowLocation, row.getRowArray(), columnToUpdate );
+
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+        finally
+        {
+            if (heapCC != null) { heapCC.close(); }
+        }
+    }
+    
+	/**
+	 * @see org.apache.derby.iapi.sql.dictionary.DataDictionary#getCurrentValueAndAdvance
+	 */
+    public void getCurrentValueAndAdvance
+        ( String sequenceUUIDstring, NumberDataValue returnValue )
+        throws StandardException
+    {
+        SequenceUpdater sequenceUpdater = null;
+
+        try {
+            sequenceUpdater = (SequenceUpdater) sequenceGeneratorCache.find( sequenceUUIDstring );
+
+            sequenceUpdater.getCurrentValueAndAdvance( returnValue );
+        }
+        finally
+        {
+            if ( sequenceUpdater != null )
+            {
+                sequenceGeneratorCache.release( sequenceUpdater );
+            }
+        }
+    }
+    
     public RowLocation getRowLocationTemplate(LanguageConnectionContext lcc,
                                               TableDescriptor td)
           throws StandardException

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SYSSEQUENCESRowFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SYSSEQUENCESRowFactory.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SYSSEQUENCESRowFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SYSSEQUENCESRowFactory.java Mon Feb  8 13:51:35 2010
@@ -45,25 +45,27 @@
 import java.sql.Types;
 
 /**
- * Factory for creating a SYSSEQUENCES row.
+ * Factory for creating a SYSSEQUENCES row. The contract of this table is this:
+ * if the CURRENTVALUE column is null, then the sequence is exhausted and
+ * no more values can be generated from it.
  */
 
-public class SYSSEQUENCESRowFactory extends CatalogRowFactory {
+public class SYSSEQUENCESRowFactory extends CatalogRowFactory
+{
+    public static final String TABLENAME_STRING = "SYSSEQUENCES";
 
-    private static final String TABLENAME_STRING = "SYSSEQUENCES";
-
-    private static final int SYSSEQUENCES_COLUMN_COUNT = 10;
+    public static final int SYSSEQUENCES_COLUMN_COUNT = 10;
     /* Column #s for sysinfo (1 based) */
-    private static final int SYSSEQUENCES_SEQUENCEID = 1;
-    private static final int SYSSEQUENCES_SEQUENCENAME = 2;
-    private static final int SYSSEQUENCES_SCHEMAID = 3;
-    private static final int SYSSEQUENCES_SEQUENCEDATATYPE = 4;
-    private static final int SYSSEQUENCES_CURRENT_VALUE = 5;
-    private static final int SYSSEQUENCES_START_VALUE = 6;
-    private static final int SYSSEQUENCES_MINIMUM_VALUE = 7;
-    private static final int SYSSEQUENCES_MAXIMUM_VALUE = 8;
-    private static final int SYSSEQUENCES_INCREMENT = 9;
-    private static final int SYSSEQUENCES_CYCLE_OPTION = 10;
+    public static final int SYSSEQUENCES_SEQUENCEID = 1;
+    public static final int SYSSEQUENCES_SEQUENCENAME = 2;
+    public static final int SYSSEQUENCES_SCHEMAID = 3;
+    public static final int SYSSEQUENCES_SEQUENCEDATATYPE = 4;
+    public static final int SYSSEQUENCES_CURRENT_VALUE = 5;
+    public static final int SYSSEQUENCES_START_VALUE = 6;
+    public static final int SYSSEQUENCES_MINIMUM_VALUE = 7;
+    public static final int SYSSEQUENCES_MAXIMUM_VALUE = 8;
+    public static final int SYSSEQUENCES_INCREMENT = 9;
+    public static final int SYSSEQUENCES_CYCLE_OPTION = 10;
 
     private static final int[][] indexColumnPositions =
             {
@@ -118,12 +120,12 @@
         String sequenceName = null;
         String schemaIdString = null;
         TypeDescriptor typeDesc = null;
-        long currentValue = 0;
+        Long currentValue = null;
         long startValue = 0;
         long minimumValue = 0;
         long maximumValue = 0;
         long increment = 0;
-        boolean cycle = false;
+        boolean canCycle = false;
 
 
         if (td != null) {
@@ -143,7 +145,7 @@
             minimumValue = sd.getMinimumValue();
             maximumValue = sd.getMaximumValue();
             increment = sd.getIncrement();
-            cycle = sd.isCycle();
+            canCycle = sd.canCycle();
         }
 
         /* Build the row to insert */
@@ -162,7 +164,10 @@
         row.setColumn(SYSSEQUENCES_SEQUENCEDATATYPE, new UserType(typeDesc));
 
         /* 5th column is CURRENTVALUE */
-        row.setColumn(SYSSEQUENCES_CURRENT_VALUE, new SQLLongint(currentValue));
+        SQLLongint curVal;
+        if ( currentValue == null ) { curVal = new SQLLongint(); }
+        else { curVal = new SQLLongint( currentValue.longValue() ); }
+        row.setColumn(SYSSEQUENCES_CURRENT_VALUE, curVal );
 
         /* 6th column is STARTVALUE */
         row.setColumn(SYSSEQUENCES_START_VALUE, new SQLLongint(startValue));
@@ -177,7 +182,7 @@
         row.setColumn(SYSSEQUENCES_INCREMENT, new SQLLongint(increment));
 
         /* 10th column is CYCLEOPTION */
-        row.setColumn(SYSSEQUENCES_CYCLE_OPTION, new SQLChar(cycle ? "Y" : "N"));
+        row.setColumn(SYSSEQUENCES_CYCLE_OPTION, new SQLChar(canCycle ? "Y" : "N"));
 
         return row;
     }
@@ -203,7 +208,7 @@
         UUID ouuid;
         String sequenceName;
         UUID suuid;
-        long currentValue;
+        Long currentValue;
         long startValue;
         long minimumValue;
         long maximumValue;
@@ -246,7 +251,8 @@
                 DataTypeDescriptor.getType(catalogType);
 
         col = row.getColumn(SYSSEQUENCES_CURRENT_VALUE);
-        currentValue = col.getLong();
+        if ( col.isNull() ) { currentValue = null; }
+        else { currentValue = new Long( col.getLong() ) ; }
 
         col = row.getColumn(SYSSEQUENCES_START_VALUE);
         startValue = col.getLong();

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceGenerator.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceGenerator.java?rev=907654&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceGenerator.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceGenerator.java Mon Feb  8 13:51:35 2010
@@ -0,0 +1,529 @@
+/*
+
+   Derby - Class org.apache.derby.impl.sql.catalog.SequenceGenerator
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you 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.
+
+ */
+package org.apache.derby.impl.sql.catalog;
+
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.services.cache.Cacheable;
+import org.apache.derby.iapi.services.cache.CacheManager;
+import org.apache.derby.iapi.services.context.ContextManager;
+import org.apache.derby.iapi.services.context.ContextService;
+import org.apache.derby.iapi.services.sanity.SanityManager;
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
+import org.apache.derby.iapi.sql.dictionary.SequenceDescriptor;
+import org.apache.derby.iapi.store.access.TransactionController;
+import org.apache.derby.iapi.types.NumberDataValue;
+import org.apache.derby.iapi.types.RowLocation;
+
+/**
+ * <p>
+ * This is a generic machine for pre-allocating ranges of sequence numbers in order
+ * to improve concurrency. The public methods are synchronized and should be brief.
+ * The caller of the methods in this class is responsible for updating values on disk
+ * when the generator is exhausted or when it needs to allocate a new range of values.
+ * </p>
+ *
+ * <p>
+ * The most used method in this class is getCurrentValueAndAdvance(). This method
+ * returns the next number in the range managed by the sequence generator. This
+ * method will raise an exception if the sequence generator is exhausted. Otherwise
+ * getCurrentValueAndAdvance() hands back a tuple of return values:
+ * </p>
+ *
+ * <blockquote>
+ * ( <i>status, currentValue, lastAllocatedValue, numberOfValuesAllocated</i> )
+ * </blockquote>
+ *
+ * <p>
+ * The <i>status</i> field takes the following values:
+ * </p>
+ *
+ * <ul>
+ * <li><b>RET_I_AM_CONFUSED</b> - This value should never be returned. If this value comes back,
+ * then the sequence generator is confused.</li>
+ * <li><b>RET_OK</b> - This means that the generator has successfully obtained a
+ * next value. That value is <i>currentValue</i>.</li>
+ * <li><b>RET_MARK_EXHAUSTED</b> - This means that the generator has reached the end of its
+ * legal range and is handing back its very last value. The caller must mark the catalogs
+ * to indicate that the range is exhausted. The very last value being handed back
+ * is <i>currentValue</i>.</li>
+ * <li><b>RET_ALLOCATE_NEW_VALUES</b> - This means that the generator has come to the end
+ * of its pre-allocated values. The caller needs to update the catalog to grab a new range of
+ * legal values and then call allocateNewRange() to tell the generator that the range was
+ * successfully allocated. The remaining values in the return tuple have these meanings:
+ *  <ul>
+ *  <li><i>currentValue</i> - This is what is expected to be the current value in the catalog before
+ *   allocating a new range. If, in fact, this is not the value in the catalog, then we are racing with
+ *   another session to drain values from the generator and update the disk. Do not update
+ *   the catalog if the value there is not <i>currentValue</i>. Instead, assume that another session
+ *   got in ahead of us and grabbed a new range of values. Simply call getCurrentValueAndAdvance()
+ *   again.</li>
+ *  <li><i>lastAllocatedValue</i> - This is the next value to write to the catalog.</li>
+ *  <li><i>numberOfValuesAllocated</i> - This is the number of values which were allocated
+ *   if we successfully updated the catalog. If we successfully updated the catalog, then we
+ *   should call allocateNewRange(), handing it this value so that it can reset its range. As a
+ *   sanity check, we also hand allocateNewRange() the <i>currentValue</i> that we were
+ *   given. The allocateNewRange() method will assume we're racing another session and will
+ *   ignore us if its sense of <i>currentValue</i> does not agree with ours.</li>
+ *  </ul>
+ * </li>
+ * </ul>
+ *
+ *
+ * <p>
+ * It may happen that getCurrentValueAndAdvance() tells its caller to allocate a new
+ * range of sequence numbers in the system catalog. If the caller successfully allocates
+ * a new range, the caller should call allocateNewRange() to tell the generator to update
+ * its internal memory of that range.
+ * </p>
+ *
+ *
+ * <p>
+ * The peekAtCurrentValue() method is provided so that unused, pre-allocated values can
+ * be flushed when the sequence generator is being discarded. The caller updates the
+ * catalog with the value returned by peekAtCurrentValue().
+ * </p>
+ *
+ */
+public class SequenceGenerator
+{
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTANTS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Default number of values to pre-allocate. In the future, we may want to provide
+     * something more sophisticated. For instance, we might want to make Derby tune
+     * this number per sequence generator or give the user the power to override Derby's
+     * decision.
+     */
+    public static final int DEFAULT_PREALLOCATION_COUNT = 5;
+
+    /** If pre-allocation drops below this level, then we need to grab another chunk of numbers */
+    private static final int PREALLOCATION_THRESHHOLD = 1;
+
+    //
+    // Return values from dispatchCommand().
+    //
+    public static final int RET_I_AM_CONFUSED = 0; // should never see this
+    public static final int RET_OK = RET_I_AM_CONFUSED + 1;
+    public static final int RET_MARK_EXHAUSTED = RET_OK + 1;
+    public static final int RET_ALLOCATE_NEW_VALUES = RET_MARK_EXHAUSTED + 1;
+
+    //
+    // Offsets into array of longs returned by getCurrentValueAndAdvance()
+    //
+    public static final int CVAA_STATUS = 0;
+    public static final int CVAA_CURRENT_VALUE = CVAA_STATUS + 1;
+    public static final int CVAA_LAST_ALLOCATED_VALUE = CVAA_CURRENT_VALUE + 1;
+    public static final int CVAA_NUMBER_OF_VALUES_ALLOCATED = CVAA_LAST_ALLOCATED_VALUE + 1;
+    public static final int CVAA_LENGTH = CVAA_NUMBER_OF_VALUES_ALLOCATED + 1;
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTANT STATE
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    //
+    // The following state is initialized when this SequenceGenerator is created.
+    //
+
+    // True if the generator can wrap-around, that is, if the generator was declared to CYCLE.
+    private final boolean _CAN_CYCLE;
+
+    // True if the increment value is positive.
+    private final boolean _STEP_INCREASES;
+
+    // This is the step-size for the generator. It is non-zero. It is positive for
+    // generators which increment and it is negative for generators which decrement.
+    private final long _INCREMENT;
+
+    // This is the highest (most positive) value which the generator is willing to hand out.
+    private final long _MAX_VALUE;
+
+    // This is lowest (most negative) value which the generator is willing to hand out.
+    private final long _MIN_VALUE;
+
+    // This is where we restart the sequence if we wrap around.
+    private final long _RESTART_VALUE;
+
+    // Name of the sequence (for error messages).
+    private final String _SEQUENCE_NAME;
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // VARIABLES
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    // True if the generator can not produce any more values.
+    private boolean _isExhausted;
+
+    // This is the next value which the generator will hand out.
+    private long _currentValue;
+
+    // This is the number of values to pre-allocate per chunk. Right now this
+    // is a constant which we figure out when we initialize the generator.
+    // However, this number could change over time if, for instance, Derby
+    // tunes it on the fly.
+    private long _valuesPerAllocation;
+    
+    // This is the remaining number of values which were pre-allocated on disk
+    // by bumping the contents of SYSSEQUENCES.CURRENTVALUE.
+    private long _remainingPreallocatedValues;
+
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTRUCTOR
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /** Normal constructor */
+    public SequenceGenerator
+        (
+         Long currentValue,
+         boolean canCycle,
+         long increment,
+         long maxValue,
+         long minValue,
+         long restartValue,
+         String sequenceName
+         )
+    {
+        if ( currentValue == null )
+        {
+            _isExhausted = true;
+            _currentValue = 0;
+        }
+        else
+        {
+            _isExhausted = false;
+            _currentValue = currentValue.longValue();
+        }
+
+        _CAN_CYCLE = canCycle;
+        _INCREMENT = increment;
+        _MAX_VALUE = maxValue;
+        _MIN_VALUE = minValue;
+        _RESTART_VALUE = restartValue;
+        _STEP_INCREASES = ( _INCREMENT > 0 );
+        _SEQUENCE_NAME = sequenceName;
+
+        //
+        // Next call to getCurrentValueAndAdvance() will cause  us to ask our caller to allocate a new range of values.
+        //
+        _remainingPreallocatedValues = 1L;
+
+        _valuesPerAllocation = computePreAllocationCount();
+    }
+    
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // PUBLIC BEHAVIOR
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Get the name of this sequence generator. Technically, this doesn't need to be
+     * synchronized. But it is simpler to just maintain a rule that all public methods
+     * should be synchronized.
+     * </p>
+     */
+    public synchronized String getName() { return _SEQUENCE_NAME; }
+    
+    /**
+     * <p>
+     * Allocate a new range. Is a NOP if the current value is not what we expected. See the
+     * class header comment for more information on how this method is used.
+     * </p>
+     */
+    public synchronized void allocateNewRange( long expectedCurrentValue, long numberOfAllocatedValues )
+    {
+        if ( _currentValue == expectedCurrentValue )
+        {
+            _remainingPreallocatedValues = numberOfAllocatedValues;
+        }
+    }
+     
+    /**
+     * <p>
+     * Peek at the current value of the sequence generator without advancing the
+     * generator. Returns null if the generator is exhausted.
+     * </p>
+     */
+    public synchronized Long peekAtCurrentValue()
+    {
+        Long currentValue = null;
+
+        if ( !_isExhausted ) { currentValue = new Long( _currentValue ); }
+        
+        return currentValue;
+    }
+    
+    /**
+     * <p>
+     * Get the next sequence number managed by this generator and advance the number. Could raise an
+     * exception if the legal range is exhausted and wrap-around is not allowed--that is,
+     * if NO CYCLE was specified when the sequence was defined. See the class header comment
+     * for a description of how this method operates.
+     * </p>
+     *
+     * @return Returns an array of longs indexed by the CVAA_* constants.
+     */
+    public synchronized long[] getCurrentValueAndAdvance()
+        throws StandardException
+    {
+        if ( _isExhausted )
+        {
+            throw StandardException.newException
+                ( SQLState.LANG_SEQUENCE_GENERATOR_EXHAUSTED, _SEQUENCE_NAME );
+        }
+
+        long retval[] = new long[ CVAA_LENGTH ];
+        retval[ CVAA_STATUS ] = RET_I_AM_CONFUSED;
+        retval[ CVAA_CURRENT_VALUE ] = _currentValue;
+
+        advanceValue( retval );
+        
+        return retval;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // PRIVATE BEHAVIOR
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+
+    /**
+     * <p>
+     * Advance the sequence generator. Pre-allocate a range of new values if
+     * necessary.
+     * </p>
+     *
+     * @param retval Array of return values to fill in: see CVAA_* constants
+     */
+    private void advanceValue( long[] retval ) throws StandardException
+    {
+        long nextValue = _currentValue + _INCREMENT;
+
+        if ( overflowed( _currentValue, nextValue ) )
+        {
+            // Take this generator offline if we've wrapped around but cycling really isn't allowed.
+            if ( !_CAN_CYCLE )
+            {
+                markExhausted( retval );
+                return;
+            }
+
+            // Otherwise, cycling is allowed.
+            nextValue = _RESTART_VALUE;
+        }
+
+        _remainingPreallocatedValues--;
+        if ( _remainingPreallocatedValues < PREALLOCATION_THRESHHOLD )
+        {
+            computeNewAllocation( _currentValue, retval );
+            return;
+        }
+        else
+        {
+            _currentValue = nextValue;
+            retval[ CVAA_STATUS ] = RET_OK;
+            return;
+        }
+    }
+
+    /**
+     * <p>
+     * Mark the generator as exhausted.
+     * </p>
+     */
+    private void markExhausted( long[] retval )
+    {
+        _isExhausted = true;
+        retval[ CVAA_STATUS ] = RET_MARK_EXHAUSTED;
+
+        return;
+    }
+
+    /**
+     * <p>
+     * Return true if an overflow/underflow occurred. This happens if
+     * the originalValue and incrementedValue have opposite sign. Overflow
+     * also occurs if the incrementedValue falls outside the range of the
+     * sequence.
+     * </p>
+     */
+    private boolean overflowed( long originalValue, long incrementedValue )
+    {
+        boolean overflowed = ( _STEP_INCREASES == ( incrementedValue < originalValue ) );
+        
+        if ( !overflowed )
+        {
+            if ( _STEP_INCREASES ) { overflowed = ( incrementedValue > _MAX_VALUE ); }
+            else { overflowed = ( incrementedValue < _MIN_VALUE ); }
+        }
+
+        return overflowed;
+    }
+
+    /**
+     * <p>
+     * Compute the number of values to allocate. The range may wrap around.
+     * </p>
+     *
+     * @param oldCurrentValue INPUT Used to compute how many values need to be allocated
+     * @param retval OUTPUT Array of values to fill in (see CVAA_* constants)
+     *
+     * @throws StandardException if any error occurs.
+     */
+    private void computeNewAllocation( long oldCurrentValue, long[] retval ) throws StandardException
+    {
+        //
+        // The values are growing toward one of the endpoints of the legal range,
+        // either the largest legal value or the smallest legal value. First find out
+        // how many values are left between the current value and the endpoint
+        // we are growing toward.
+        //
+        long remainingLegalValues = computeRemainingValues( oldCurrentValue );
+
+        long newValueOnDisk;
+        long valuesToAllocate;
+
+        if ( remainingLegalValues >= _valuesPerAllocation )
+        {
+            newValueOnDisk = oldCurrentValue + ( _valuesPerAllocation * _INCREMENT );
+            valuesToAllocate = _valuesPerAllocation;
+        }
+        else
+        {
+            // We wrapped around.
+
+            if ( _CAN_CYCLE )
+            {
+                long spillOverValues = _valuesPerAllocation - remainingLegalValues;
+
+                // account for the fact that the restart value itself is a legal value
+                spillOverValues--;
+
+                newValueOnDisk = _RESTART_VALUE + ( spillOverValues * _INCREMENT );
+                valuesToAllocate = _valuesPerAllocation;
+            }
+            else
+            {
+                // wrap around not allowed
+                
+                if ( remainingLegalValues <= 0 )
+                {
+                    markExhausted( retval );
+                    return;
+                }
+                else
+                {
+                    valuesToAllocate = remainingLegalValues;
+                    newValueOnDisk = oldCurrentValue + ( valuesToAllocate * _INCREMENT );
+                }
+            }
+        }
+
+        //account for the fact that the current value is already allocated
+        retval[ CVAA_NUMBER_OF_VALUES_ALLOCATED ] = valuesToAllocate + 1;
+        retval[ CVAA_LAST_ALLOCATED_VALUE ] = newValueOnDisk ;
+        retval[ CVAA_STATUS ] = RET_ALLOCATE_NEW_VALUES;
+    }
+    
+    /**
+     * <p>
+     * Get the number of values remaining until we bump against an endpoint of the legal range of values.
+     * This is a positive number and so may understate the number of remaining values if the datatype is BIGINT.
+     * </p>
+     *
+     */
+    private long computeRemainingValues( long oldCurrentValue )
+    {
+        long spaceLeft = _STEP_INCREASES ? _MAX_VALUE - oldCurrentValue : -( _MIN_VALUE - oldCurrentValue );
+
+        // if overflow occurred, the range is very large
+        if ( spaceLeft < 0L ) { spaceLeft = Long.MAX_VALUE; }
+
+        long divisor = _STEP_INCREASES ? _INCREMENT : -_INCREMENT;
+
+        return spaceLeft / divisor;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // SETUP/TEARDOWN MINIONS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * This method returns the number of values to pre-allocate when we
+     * grab a new chunk of values. This is a bit of defensive coding to cover
+     * the case when the sequence's parameters are absurdly large.
+     * </p>
+     */
+    private int computePreAllocationCount()
+    {
+        int happyResult = DEFAULT_PREALLOCATION_COUNT;
+        int unhappyResult = PREALLOCATION_THRESHHOLD;
+
+        double min = _MIN_VALUE;
+        double max = _MAX_VALUE;
+        double range = max - min;
+        double step = _INCREMENT;
+        if ( step < 0.0 ) { step = -step; }
+
+        double chunkSize = step * happyResult;
+
+        if ( chunkSize > Long.MAX_VALUE ) { return unhappyResult; }
+        if ( chunkSize > range ) { return unhappyResult; }
+
+        return happyResult;
+    }
+
+    
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // UTILITY MINIONS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+	private static LanguageConnectionContext getLCC()
+    {
+		return (LanguageConnectionContext) 
+					ContextService.getContextOrNull(LanguageConnectionContext.CONTEXT_ID);
+	}
+
+    /** Report an unimplemented feature */
+    private StandardException unimplementedFeature()
+    {
+        return StandardException.newException( SQLState.BTREE_UNIMPLEMENTED_FEATURE );
+    }
+
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceGenerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java?rev=907654&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java Mon Feb  8 13:51:35 2010
@@ -0,0 +1,479 @@
+/*
+
+   Derby - Class org.apache.derby.impl.sql.catalog.SequenceUpdater
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you 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.
+
+ */
+package org.apache.derby.impl.sql.catalog;
+
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.services.cache.Cacheable;
+import org.apache.derby.iapi.services.cache.CacheManager;
+import org.apache.derby.iapi.services.context.ContextManager;
+import org.apache.derby.iapi.services.context.ContextService;
+import org.apache.derby.iapi.services.sanity.SanityManager;
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
+import org.apache.derby.iapi.sql.dictionary.SequenceDescriptor;
+import org.apache.derby.iapi.store.access.TransactionController;
+import org.apache.derby.iapi.types.NumberDataValue;
+import org.apache.derby.iapi.types.RowLocation;
+
+/**
+ * <p>
+ * An object cached in the data dictionary which manages new values
+ * for sequences. Note that this class must be public and
+ * have a 0-arg constructor in order to satisfy the Cacheable contract.
+ * </p>
+ *
+ * <p>
+ * This is the abstract superclass of specific implementations for specific
+ * sequences. For instance, one subclass handles the ANSI/ISO sequences
+ * stored in SYSSEQUENCES. Another subclass could handle the sequences
+ * stored in Derby's identity columns.
+ * </p>
+ *
+ * <p>
+ * This class does a couple tricky things:
+ * </p>
+ *
+ * <ul>
+ * <li>It pre-allocates a range of values from a sequence so that we don't have to change
+ *  the on-disk value every time we get the next value for a sequence.</li>
+ * <li>When updating the on-disk value, we first try to do the writing in
+ *  a nested subtransaction. This is so that we can immediately release the write-lock afterwards.
+ *  If that fails, we then try to do the writing in the user's execution transaction.</li>
+ * </ul>
+ *
+ * <p>
+ * Here is the algorithm pursued when the caller asks for the next number in a sequence:
+ * </p>
+ *
+ *
+ * <ul>
+ * <li>We try to get the next number from a cache of pre-allocated numbers. The endpoint
+ * (last number in the pre-allocated range) was previously recorded in the catalog row which
+ * describes this sequence. If we are successful in getting the next number, we
+ * return it and all is well.</li>
+ * <li>Otherwise, we must allocate a new range by updating the catalog row. At this
+ * point we may find ourselves racing another session, which also needs the next number
+ * in the sequence.</li>
+ * <li>When we try to update the catalog row, we check to see whether the current value
+ * there is what we expect it to be. If it is, then all is well: we update the catalog row
+ * then return to the first step to try to get the next number from the new cache of
+ * pre-allocated numbers.</li>
+ * <li>If, however, the value in the catalog row is not what we expect, then another
+ * session has won the race to update the catalog. We accept this fact gracefully and
+ * do not touch the catalog. Instead, we return to the first step and try to get the
+ * next number from the new cache of numbers which the other session has just
+ * pre-allocated.</li>
+ * <li>We only allow ourselves to retry this loop a small number of times. If we still
+ * can't get the next number in the sequence, we raise an exception complaining that
+ * there is too much contention on the generator.</li>
+ * </ul>
+ *
+ * <p>
+ * If applications start seeing exceptions complaining that there is too much contention
+ * on a sequence generator, then we should improve this algorithm. Here are some options
+ * based on the idea that contention should go down if we increase the number of
+ * pre-allocated numbers:
+ * </p>
+ *
+ * <ul>
+ * <li>We can let the user change the size of the pre-allocated range.</li>
+ * <li>Derby can increase the size of the pre-allocated range when Derby detects
+ * too much contention.</li>
+ * </ul>
+ *
+ */
+public abstract class SequenceUpdater implements Cacheable
+{
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTANTS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    //
+    // This is the number of times we attempt to get a sequence number in case we
+    // find ourselves in a race against another session which is draining numbers
+    // from the same sequence generator.
+    //
+    private static final int RETRY_COUNT = 3;
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTANT STATE
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    // DataDictionary where this generator is cached.
+    protected DataDictionaryImpl _dd;
+
+    // This is the key used to lookup this generator in the cache.
+    protected String _uuidString;
+
+    // This is the object which allocates ranges of sequence values */
+    protected SequenceGenerator _sequenceGenerator;
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTRUCTOR
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /** No-arg constructor to satisfy the Cacheable contract */
+    public SequenceUpdater() {}
+
+    /** Normal constructor */
+    public SequenceUpdater( DataDictionaryImpl dd )
+    {
+        _dd = dd;
+    }
+    
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // ABSTRACT BEHAVIOR TO BE IMPLEMENTED BY CHILDREN
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Initialize the sequence generator. Work is done inside a read-only subtransaction of
+     * the session's execution transaction.
+     * </p>
+     */
+     abstract protected SequenceGenerator createSequenceGenerator( TransactionController readOnlyTC )
+         throws StandardException;
+
+    /**
+     * <p>
+     * Update the sequence value on disk. This method is first called with a read/write subtransaction
+     * of the session's execution transaction. If work can't be done there immediately, this method
+     * is called with the session's execution transaction.
+     * </p>
+     *
+     * @param tc The transaction to use
+     * @param oldValue Expected value on disk for this sequence
+     * @param newValue The value to poke into the system table backing this sequence
+     * @param wait Whether to wait for a lock
+     *
+	 * 
+	 * @return Returns true if the value was successfully updated, false if we lost a race with another session.
+     *
+     * @throws StandardException May throw an exception if a lock can't be obtained.
+     */
+    abstract protected boolean updateCurrentValueOnDisk( TransactionController tc, Long oldValue, Long newValue, boolean wait ) throws StandardException;
+    
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // Cacheable BEHAVIOR
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+	public void clean(boolean forRemove) throws StandardException
+	{
+        //
+        // Flush current value to disk. This prevents us from leaking values when DDL
+        // is performed. The metadata caches are invalidated and cleared when DDL is performed.
+        //
+        if ( _sequenceGenerator != null )
+        {
+            updateCurrentValueOnDisk( null, peekAtCurrentValue() );
+        }
+
+        _uuidString = null;
+        _sequenceGenerator = null;
+	}
+    
+	public boolean isDirty() { return false; }
+    public Object getIdentity() { return _uuidString; }
+
+	public void clearIdentity()
+    {
+        try
+        {
+            clean( false );
+        } catch (StandardException se)
+        {
+            getLCC().getContextManager().cleanupOnError( se );
+        }
+    }
+
+	public Cacheable createIdentity( Object key, Object createParameter ) throws StandardException
+	{
+        Cacheable cacheable = this;
+
+        //
+        // The createParameter arg is unused.
+        //
+        return cacheable.setIdentity( key );
+	}
+
+	/**
+	 * @see Cacheable#setIdentity
+	 *
+	 * @exception StandardException		Thrown on error
+	 */
+	public Cacheable setIdentity(Object key) throws StandardException
+	{
+		if (SanityManager.DEBUG)
+		{
+			if (!(key instanceof String))
+			{
+				SanityManager.THROWASSERT( "Key for a SequenceUpdater is a " + key.getClass().getName() );
+			}
+
+            if ( (_uuidString != null) || (_sequenceGenerator != null) )
+			{
+				SanityManager.THROWASSERT( "Identity being changed on a live cacheable. Old uuidString = " + _uuidString );
+			}
+		}
+
+		_uuidString = (String) key;
+
+        if ( _sequenceGenerator == null )
+        {
+            TransactionController executionTC = getLCC().getTransactionExecute();
+            
+            //
+            // We lookup information in a read-only subtransaction in order to minimize
+            // contention. Since this is a read-only subtransaction, there should be
+            // no conflict with the parent transaction.
+            //
+            TransactionController subTransaction = executionTC.startNestedUserTransaction( true );
+            try {
+                _sequenceGenerator = createSequenceGenerator( subTransaction );
+            }
+            finally
+            {
+                subTransaction.commit();
+                subTransaction.destroy();
+            }
+        }
+
+		if ( _sequenceGenerator != null ) { return this; }
+		else { return null; }
+	}
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // PUBLIC BEHAVIOR
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Get the next sequence number managed by this generator and advance the number. Could raise an
+     * exception if the legal range is exhausted and wrap-around is not allowed.
+     * Only one thread at a time is allowed through here. That synchronization is performed by
+     * the sequence generator itself.
+     * </p>
+     *
+     * @param returnValue This value is stuffed with the new sequence number.
+     */
+    public void getCurrentValueAndAdvance
+        ( NumberDataValue returnValue ) throws StandardException
+    {
+        //
+        // We try to get a sequence number. We try a couple times in case we find
+        // ourselves in a race with another session which is draining numbers from
+        // the same sequence generator.
+        //
+        for ( int i = 0; i < RETRY_COUNT; i++ )
+        {
+            long[] cvaa = _sequenceGenerator.getCurrentValueAndAdvance();
+            
+            int status = (int) cvaa[ SequenceGenerator.CVAA_STATUS ];
+            long currentValue = cvaa[ SequenceGenerator.CVAA_CURRENT_VALUE ];
+            long lastAllocatedValue = cvaa[ SequenceGenerator.CVAA_LAST_ALLOCATED_VALUE ];
+            long numberOfValuesAllocated = cvaa[ SequenceGenerator.CVAA_NUMBER_OF_VALUES_ALLOCATED ];
+            
+            switch ( status )
+            {
+            case SequenceGenerator.RET_OK:
+                returnValue.setValue( currentValue );
+                return;
+                
+            case SequenceGenerator.RET_MARK_EXHAUSTED:
+                updateCurrentValueOnDisk( new Long( currentValue ), null );
+                returnValue.setValue( currentValue );
+                return;
+                
+            case SequenceGenerator.RET_ALLOCATE_NEW_VALUES:
+                
+                if ( updateCurrentValueOnDisk( new Long( currentValue ), new Long( lastAllocatedValue ) ) )
+                {
+                    _sequenceGenerator.allocateNewRange( currentValue, numberOfValuesAllocated );
+                }
+                break;
+                
+            default:
+                throw unimplementedFeature();
+            }
+
+            //
+            // If we get here, then we failed to get a sequence number. Along the way,
+            // we or another session may have allocated more sequence numbers on disk. We go back
+            // in to try to grab one of those numbers.
+            //
+
+            
+        } // end of retry loop
+
+        //
+        // If we get here, then we exhausted our retry attempts. This might be a sign
+        // that we need to increase the number of sequence numbers which we
+        // allocate. There's an opportunity for Derby to tune itself here.
+        //
+        throw StandardException.newException
+            ( SQLState.LANG_TOO_MUCH_CONTENTION_ON_SEQUENCE, _sequenceGenerator.getName() );
+    }
+
+    /**
+     * <p>
+     * Get the current value of the sequence generator without advancing it.
+     * May return null if the generator is exhausted.
+     * </p>
+     */
+    private Long peekAtCurrentValue() throws StandardException
+    {
+        return _sequenceGenerator.peekAtCurrentValue();
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // DISK WRITING MINIONS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Update the value on disk. First tries to update the value in a
+     * subtransaction. If that fails, falls back on the execution transaction.
+     * This is a callback method invoked by the sequence generator.
+     * </p>
+	 * 
+	 * @return Returns true if the value was successfully updated, false if we lost a race with another session.
+     *
+     */
+    public boolean updateCurrentValueOnDisk( Long oldValue, Long newValue ) throws StandardException
+    {
+        TransactionController executionTransaction = getLCC().getTransactionExecute();
+        TransactionController nestedTransaction = null;
+
+        // Try to get a nested transaction.
+        try {
+            nestedTransaction = executionTransaction.startNestedUserTransaction( false );
+        } catch (StandardException se) {}
+
+        // First try to do the work in the nested transaction. Fail if we can't
+        // get a lock immediately.
+        if ( nestedTransaction != null )
+        {
+            try {
+                return updateCurrentValueOnDisk( nestedTransaction, oldValue, newValue, false );
+            }
+            catch (StandardException se)
+            {
+                if ( !se.getMessageId().equals( SQLState.LOCK_TIMEOUT ) ) { throw se; }
+            }
+            finally
+            {
+                nestedTransaction.commit();
+                nestedTransaction.destroy();
+            }
+        }
+
+        // If we get here, we failed to do the work in the nested transaction.
+        // Fall back on the execution transaction
+        return updateCurrentValueOnDisk( executionTransaction, oldValue, newValue, true );
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // UTILITY MINIONS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+	private static LanguageConnectionContext getLCC()
+    {
+		return (LanguageConnectionContext) 
+					ContextService.getContextOrNull(LanguageConnectionContext.CONTEXT_ID);
+	}
+
+    /** Report an unimplemented feature */
+    private StandardException unimplementedFeature()
+    {
+        return StandardException.newException( SQLState.BTREE_UNIMPLEMENTED_FEATURE );
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // INNER CLASSES
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Specific implementation of SequenceUpdater for the sequences managed by
+     * SYSSEQUENCES.
+     * </p>
+     */
+    public static final class SyssequenceUpdater extends SequenceUpdater
+    {
+        private RowLocation _sequenceRowLocation;
+
+        public SyssequenceUpdater() { super(); }
+        public SyssequenceUpdater( DataDictionaryImpl dd ) { super( dd ); }
+    
+        //
+        // SequenceUpdater BEHAVIOR
+        //
+
+        protected SequenceGenerator createSequenceGenerator( TransactionController readOnlyTC )
+            throws StandardException
+        {
+            RowLocation[] rowLocation = new RowLocation[ 1 ];
+            SequenceDescriptor[] sequenceDescriptor = new SequenceDescriptor[ 1 ];
+            
+            _dd.computeSequenceRowLocation( readOnlyTC, _uuidString, rowLocation, sequenceDescriptor );
+            
+            _sequenceRowLocation = rowLocation[ 0 ];
+            
+            SequenceDescriptor isd = sequenceDescriptor[ 0 ];
+            
+            return new SequenceGenerator
+                (
+                 isd.getCurrentValue(),
+                 isd.canCycle(),
+                 isd.getIncrement(),
+                 isd.getMaximumValue(),
+                 isd.getMinimumValue(),
+                 isd.getStartValue(),
+                 isd.getSequenceName()
+                 );
+        }
+
+        protected boolean updateCurrentValueOnDisk( TransactionController tc, Long oldValue, Long newValue, boolean wait ) throws StandardException
+        {
+            return _dd.updateCurrentSequenceValue( tc, _sequenceRowLocation, wait, oldValue, newValue );
+        }
+    }
+
+}
+

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/SequenceUpdater.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NextSequenceNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NextSequenceNode.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NextSequenceNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NextSequenceNode.java Mon Feb  8 13:51:35 2010
@@ -21,9 +21,12 @@
 package org.apache.derby.impl.sql.compile;
 
 import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.reference.ClassName;
 import org.apache.derby.iapi.services.sanity.SanityManager;
 import org.apache.derby.iapi.services.compiler.MethodBuilder;
 import org.apache.derby.iapi.services.compiler.LocalField;
+import org.apache.derby.iapi.services.classfile.VMOpcode;
 import org.apache.derby.iapi.sql.dictionary.SequenceDescriptor;
 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
 
@@ -69,6 +72,11 @@
         SchemaDescriptor sd = getSchemaDescriptor(sequenceName.getSchemaName());
         sequenceDescriptor = getDataDictionary().getSequenceDescriptor(sd, sequenceName.getTableName());
 
+        if ( sequenceDescriptor == null )
+        {
+                throw StandardException.newException(SQLState.LANG_OBJECT_NOT_FOUND, "SEQUENCE", sequenceName.getFullTableName());
+        }
+
         // set the datatype of the value node
         this.setType(sequenceDescriptor.getDataType());
 
@@ -88,17 +96,26 @@
 
 
     public void generateExpression
+        (
+         ExpressionClassBuilder acb,
+         MethodBuilder mb
+         )
+        throws StandardException
+    {
+        String sequenceUUIDstring = sequenceDescriptor.getUUID().toString();
+        int dataTypeFormatID = sequenceDescriptor.getDataType().getNull().getTypeFormatId();
+        
+		mb.pushThis();
+		mb.push( sequenceUUIDstring );
+		mb.push( dataTypeFormatID );
+		mb.callMethod
             (
-                    ExpressionClassBuilder acb,
-                    MethodBuilder mb
-            ) throws StandardException {
-
-        //TODO : Proper implementation for value generation
-        generateConstant(acb, mb);    //dummy method to return a constant
-
-        acb.generateDataValue(mb, getTypeCompiler(),
-                getTypeServices().getCollationType(), (LocalField) null);
-
+             VMOpcode.INVOKEVIRTUAL,
+             ClassName.BaseActivation,
+             "getCurrentValueAndAdvance",
+             ClassName.NumberDataValue,
+             2
+             );
     }
 
     /**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java Mon Feb  8 13:51:35 2010
@@ -75,6 +75,7 @@
 import org.apache.derby.iapi.types.DataValueFactory;
 import org.apache.derby.iapi.types.NumberDataValue;
 import org.apache.derby.iapi.types.RowLocation;
+import org.apache.derby.iapi.types.StringDataValue;
 import org.apache.derby.iapi.util.ReuseFactory;
 
 /**
@@ -662,6 +663,27 @@
 	}
 
 	/**
+	 * Called by generated code to get the next number in an ANSI/ISO sequence
+     * and advance the sequence. Raises an exception if the sequence was declared
+     * NO CYCLE and its range is exhausted.
+	 *
+     * @param sequenceUUIDstring The string value of the sequence's UUID
+     * @param typeFormatID The format id of the data type to be returned. E.g., StoredFormatIds.SQL_INTEGER_ID.
+     *
+	 * @return The next number in the sequence
+	 */
+	protected NumberDataValue getCurrentValueAndAdvance
+        ( String sequenceUUIDstring, int typeFormatID )
+	       throws StandardException
+	{
+        NumberDataValue ndv = (NumberDataValue) getDataValueFactory().getNull( typeFormatID, StringDataValue.COLLATION_TYPE_UCS_BASIC );
+
+        lcc.getDataDictionary().getCurrentValueAndAdvance( sequenceUUIDstring, ndv );
+
+        return ndv;
+	}
+
+	/**
 		Used in CurrentOfResultSet to get to the cursor result set
 		for a cursor.  Overridden by activations generated for
 		updatable cursors.  Those activations capture the cursor

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateSequenceConstantAction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateSequenceConstantAction.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateSequenceConstantAction.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateSequenceConstantAction.java Mon Feb  8 13:51:35 2010
@@ -121,7 +121,7 @@
                 dd.getUUIDFactory().createUUID(),
                 _sequenceName,
                 _dataType,
-                _initialValue,   // current value
+                new Long( _initialValue ),   // current value
                 _initialValue,
                 _minValue,
                 _maxValue,

Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml Mon Feb  8 13:51:35 2010
@@ -653,6 +653,12 @@
             </msg>
 
             <msg>
+                <name>2200H.S</name>
+                <text>Sequence generator '{0}' does not cycle. No more values can be obtained from this sequence generator.</text>
+                <arg>sequenceName</arg>
+            </msg>
+
+            <msg>
                 <name>2200L</name>
                 <text>Values assigned to XML columns must be well-formed DOCUMENT nodes.</text>
             </msg>
@@ -3392,6 +3398,12 @@
                 <arg>id</arg>
             </msg>
 
+            <msg>
+                <name>X0Y84.S</name>
+                <text>Too much contention on sequence {0}.</text>
+                <arg>sequenceName</arg>
+            </msg>
+
         </family>
 
 

Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java (original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java Mon Feb  8 13:51:35 2010
@@ -699,6 +699,7 @@
 	String LANG_DATE_RANGE_EXCEPTION                                   = "22007.S.180";
 	String LANG_DATE_SYNTAX_EXCEPTION                                  = "22007.S.181";
     String LANG_INVALID_FUNCTION_ARGUMENT                              = "22008.S";
+    String LANG_SEQUENCE_GENERATOR_EXHAUSTED                              = "2200H.S";
 	String LANG_SUBSTR_START_OR_LEN_OUT_OF_RANGE                        = "22011";
 	String LANG_SUBSTR_START_ADDING_LEN_OUT_OF_RANGE                        = "22011.S.1";
 	String LANG_DIVIDE_BY_ZERO                                         = "22012";
@@ -1358,6 +1359,7 @@
 	String LANG_INVALID_CALL_TO_EXECUTE_UPDATE		                   = "X0Y79.S";
 	String LANG_NULL_DATA_IN_NON_NULL_COLUMN               	   	   	   = "X0Y80.S";
     String LANG_IGNORE_MISSING_INDEX_ROW_DURING_DELETE                 = "X0Y83.S";
+    String LANG_TOO_MUCH_CONTENTION_ON_SEQUENCE                 = "X0Y84.S";
 
 
 	// TEMPORARY EXECUTION RESTRICTIONS

Modified: db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java (original)
+++ db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java Mon Feb  8 13:51:35 2010
@@ -683,6 +683,13 @@
 		return null;
 	}
 
+    public void getCurrentValueAndAdvance
+        ( String sequenceUUIDstring, NumberDataValue returnValue )
+        throws StandardException
+    {
+		// TODO Auto-generated method stub
+    }
+    
 	public RowLocation getRowLocationTemplate(LanguageConnectionContext lcc,
 			TableDescriptor td) throws StandardException {
 		// TODO Auto-generated method stub

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GeneratedColumnsHelper.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GeneratedColumnsHelper.java?rev=907654&r1=907653&r2=907654&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GeneratedColumnsHelper.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GeneratedColumnsHelper.java Mon Feb  8 13:51:35 2010
@@ -56,6 +56,8 @@
     //
     ///////////////////////////////////////////////////////////////////////////////////
 
+    protected static final    String OBJECT_DOES_NOT_EXIST = "42X94";
+    protected static final    String NONEXISTENT_OBJECT = "42Y55";
     protected static  final   String  REDUNDANT_CLAUSE = "42613";
     protected static  final   String  CANT_CONTAIN_NULLS = "42831";
     protected static  final   String  ILLEGAL_AGGREGATE = "42XA1";

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SGVetter.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SGVetter.java?rev=907654&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SGVetter.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SGVetter.java Mon Feb  8 13:51:35 2010
@@ -0,0 +1,200 @@
+/*
+
+   Derby - Class org.apache.derbyTesting.functionTests.tests.lang.SequenceGeneratorTest
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you 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.
+
+ */
+
+package org.apache.derbyTesting.functionTests.tests.lang;
+
+import java.math.BigInteger;
+
+/**
+ * <p>
+ * Machine to validate the operation of the sequence generator. This is a
+ * re-implementation of the sequence generator in a less efficient style whose
+ * correctness is easier to reason about.
+ * </p>
+ */
+public class SGVetter
+{
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTANTS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    private static final long MINIMUM_CACHED_VALUE_COUNT = 1L;
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // STATE
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    private boolean _CAN_CYCLE;
+    private BigInteger _STEP;
+    private BigInteger _MAX;
+    private BigInteger _MIN;
+    private BigInteger _RESTART;
+    private long _ALLOCATION_COUNT;
+    private boolean _INCREASING;
+
+    private BigInteger _currentValue;
+    private long        _valuesRemaining;
+    private BigInteger _upperBound;
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTRUCTOR
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    public SGVetter
+        (
+         Long currentValue,
+         boolean canCycle,
+         long step,
+         long max,
+         long min,
+         long restart,
+         long allocationCount
+         )
+        throws Exception
+    {
+        if ( step >= max ) { throw new IllegalArgumentException(); }
+        if ( step <= min ) { throw new IllegalArgumentException(); }
+        if ( restart > max ) { throw new IllegalArgumentException(); }
+        if ( restart < min ) { throw new IllegalArgumentException(); }
+
+        if ( currentValue != null )
+        {
+            if ( currentValue.longValue() > max ) { throw new IllegalArgumentException(); }
+            if ( currentValue.longValue() < min ) { throw new IllegalArgumentException(); }
+        }
+
+        if ( currentValue == null ) { _currentValue = null; }
+        else { _currentValue = BigInteger.valueOf( currentValue.longValue() ); }
+        
+        _CAN_CYCLE = canCycle;
+        _STEP = BigInteger.valueOf( step );
+        _MAX = BigInteger.valueOf( max );
+        _MIN = BigInteger.valueOf( min );
+        _RESTART = BigInteger.valueOf( restart );
+        _ALLOCATION_COUNT = allocationCount;
+        _INCREASING = (_STEP.compareTo( BigInteger.valueOf( 0L ) ) > 0);
+
+        _upperBound = _currentValue;
+        _valuesRemaining = MINIMUM_CACHED_VALUE_COUNT;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // BEHAVIOR
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Get the next value in the sequence. Returns null if the sequence is exhausted.
+     * </p>
+     */
+    public Long getNextValue()
+    {
+        if ( _currentValue == null ) { return null; }
+
+        BigInteger retval = cloneBigInteger( _currentValue );
+
+        advance();
+
+        return new Long( retval.longValue() );
+    }
+
+    /** Get the upper bound */
+    public Long getUpperBound()
+    {
+        if ( _upperBound == null ) { return null; }
+        else { return new Long( _upperBound.longValue() ); }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // LOGIC TO ADVANCE THE SEQUENCE
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    private void advance()
+    {
+        BigInteger newValue = bump( _currentValue );
+
+        boolean overflowed = ( newValue.compareTo( _currentValue ) == 0 );
+
+        if ( overflowed && !_CAN_CYCLE )
+        {
+            _currentValue = null;
+            _upperBound = null;
+            return;
+        }
+
+        _valuesRemaining--;
+        if ( _valuesRemaining < MINIMUM_CACHED_VALUE_COUNT )
+        {
+            for ( long i = 0; i < _ALLOCATION_COUNT; i++ )
+            {
+                _upperBound = bump( _upperBound );
+            }
+
+            _valuesRemaining = _ALLOCATION_COUNT;
+        }
+
+        _currentValue = newValue;
+    }
+
+    // increment the original value. if this overflows and cycling is not allowed
+    // return the original value.
+    private BigInteger bump( BigInteger original )
+    {
+        BigInteger newValue = original.add( _STEP );
+
+        if ( overflowed( newValue ) )
+        {
+            if ( !_CAN_CYCLE ) { newValue = cloneBigInteger( original ); }
+            else { newValue = _RESTART; }
+        }
+
+        return newValue;
+    }
+
+    private boolean overflowed( BigInteger newValue )
+    {
+        boolean overflowed = _INCREASING ? ( newValue.compareTo( _MAX ) > 0 ) : ( newValue.compareTo( _MIN ) < 0 );
+
+        return overflowed;
+    }
+    
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // MINIONS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    private BigInteger cloneBigInteger( BigInteger original )
+    {
+        return new BigInteger( original.toByteArray() );
+    }
+}
+

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/SGVetter.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message