db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mi...@apache.org
Subject svn commit: r633560 [1/2] - in /db/derby/code/trunk/java: engine/org/apache/derby/ engine/org/apache/derby/catalog/ engine/org/apache/derby/catalog/types/ engine/org/apache/derby/iapi/services/io/ engine/org/apache/derby/iapi/sql/dictionary/ engine/org...
Date Tue, 04 Mar 2008 17:39:00 GMT
Author: mikem
Date: Tue Mar  4 09:38:48 2008
New Revision: 633560

URL: http://svn.apache.org/viewvc?rev=633560&view=rev
Log:
DERBY-3330
committed on behalf of Anurag Shekhar

Committed modified versions of patch derby-3330v13.diff and 
db2Compatibility-v2.diff.  The modifications are mostly changed and/or
added comments along with some code formatting changes to make the 
new code match the surrounding code style.

This checkin adds the functionality to
create unique constraints on a key that contains one or more nullable
columns.  This constraint will all inserts of all keys that contain
one or more columns with the null value.  All keys that contain no columns
with null values are constrained to be unique in the table.  

The underlying implementation for this new type of unique constraint uses
the existing btree non-unique index with a small modification to do 
checking at insert time to provide the described unique behavior for null
and non-null keys.  The sorter has also been modified to provide this
support for creating the index.


Added:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UniqueWithDuplicateNullsIndexSortObserver.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I_10_3.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/UniqueWithDuplicateNullsExternalSortFactory.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/UniqueWithDuplicateNullsMergeSort.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NullableUniqueConstraintTest.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/catalog/IndexDescriptor.java
    db/derby/code/trunk/java/engine/org/apache/derby/catalog/types/IndexDescriptorImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/RegisteredFormatIds.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/StoredFormatIds.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ConglomerateDescriptor.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/IndexRowGenerator.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/AccessFactoryGlobals.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/RawStoreFactory.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/compile/CreateIndexNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableElementList.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateIndexConstantAction.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericConstantActionFactory.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/IndexChanger.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTree.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeController.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2IFactory.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/ExternalSortFactory.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/MergeSort.java
    db/derby/code/trunk/java/engine/org/apache/derby/modules.properties
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/altertable.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/db2Compatibility.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/subqueryFlattening.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/PrimaryKeyTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/db2Compatibility.sql
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/subqueryFlattening.sql

Modified: db/derby/code/trunk/java/engine/org/apache/derby/catalog/IndexDescriptor.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/catalog/IndexDescriptor.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/catalog/IndexDescriptor.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/catalog/IndexDescriptor.java Tue Mar  4 09:38:48 2008
@@ -37,6 +37,11 @@
 	 * Returns true if the index is unique.
 	 */
 	boolean			isUnique();
+	/**
+	 * Returns true if the index is duplicate keys only for null key parts. 
+     * This is effective only if isUnique is false.
+	 */
+	boolean			isUniqueWithDuplicateNulls();
 
 	/**
 	 * Returns an array of column positions in the base table.  Each index

Modified: db/derby/code/trunk/java/engine/org/apache/derby/catalog/types/IndexDescriptorImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/catalog/types/IndexDescriptorImpl.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/catalog/types/IndexDescriptorImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/catalog/types/IndexDescriptorImpl.java Tue Mar  4 09:38:48 2008
@@ -58,30 +58,46 @@
 	private boolean[]	isAscending;
 	private int			numberOfOrderedColumns;
 	private String		indexType;
+	//attribute to indicate the indicates allows duplicate only in
+	//case of non null keys. This attribute has no effect if the isUnique
+    //is true. If isUnique is false and isUniqueWithDuplicateNulls is set 
+    //to true the index will allow duplicate nulls but for non null keys 
+    //will act like a unique index.
+	private boolean     isUniqueWithDuplicateNulls;
 
 	/**
-	 * Constructor for an IndexDescriptorImpl
-	 *
-	 * @param indexType		The type of index
-	 * @param isUnique		True means the index is unique
-	 * @param baseColumnPositions	An array of column positions in the base
-	 *								table.  Each index column corresponds to a
-	 *								column position in the base table.
-	 * @param isAscending	An array of booleans telling asc/desc on each
-	 *						column.
-	 * @param numberOfOrderedColumns	In the future, it will be possible
-	 *									to store non-ordered columns in an
-	 *									index.  These will be useful for
-	 *									covered queries.
-	 */
+     * Constructor for an IndexDescriptorImpl
+     * 
+     * @param indexType		The type of index
+     * @param isUnique		True means the index is unique
+     * @param isUniqueWithDuplicateNulls True means the index will be unique
+     *                              for non null values but duplicate nulls
+     *                              will be allowed.
+     *                              This parameter has no effect if the isUnique
+     *                              is true. If isUnique is false and 
+     *                              isUniqueWithDuplicateNulls is set to true the
+     *                              index will allow duplicate nulls but for
+     *                              non null keys will act like a unique index.
+     * @param baseColumnPositions	An array of column positions in the base
+     * 								table.  Each index column corresponds to a
+     * 								column position in the base table.
+     * @param isAscending	An array of booleans telling asc/desc on each
+     * 						column.
+     * @param numberOfOrderedColumns	In the future, it will be possible
+     * 									to store non-ordered columns in an
+     * 									index.  These will be useful for
+     * 									covered queries.
+     */
 	public IndexDescriptorImpl(String indexType,
 								boolean isUnique,
+								boolean isUniqueWithDuplicateNulls,
 								int[] baseColumnPositions,
 								boolean[] isAscending,
 								int numberOfOrderedColumns)
 	{
 		this.indexType = indexType;
 		this.isUnique = isUnique;
+		this.isUniqueWithDuplicateNulls = isUniqueWithDuplicateNulls;
 		this.baseColumnPositions = baseColumnPositions;
 		this.isAscending = isAscending;
 		this.numberOfOrderedColumns = numberOfOrderedColumns;
@@ -92,6 +108,16 @@
 	{
 	}
 
+	/**
+     * 
+     * 
+     * @see IndexDescriptor#isUniqueWithDuplicateNulls
+     */
+	public boolean isUniqueWithDuplicateNulls()
+	{
+		return isUniqueWithDuplicateNulls;
+	}
+
 	/** @see IndexDescriptor#isUnique */
 	public boolean isUnique()
 	{
@@ -189,6 +215,8 @@
 
 		if (isUnique)
 			sb.append("UNIQUE ");
+		else if (isUniqueWithDuplicateNulls)
+			sb.append ("ALMOST UNIQUE");
 
 		sb.append(indexType);
 
@@ -230,6 +258,13 @@
 		}
 		numberOfOrderedColumns = fh.getInt("orderedColumns");
 		indexType = (String)fh.get("indexType");
+		//isUniqueWithDuplicateNulls attribute won't be present if the index
+		//was created in older versions  
+		if (fh.containsKey("isUniqueWithDuplicateNulls"))
+			isUniqueWithDuplicateNulls = fh.getBoolean(
+                                    "isUniqueWithDuplicateNulls");
+		else
+			isUniqueWithDuplicateNulls = false;
 	}
 
 	/**
@@ -249,7 +284,10 @@
 		}
 		fh.putInt("orderedColumns", numberOfOrderedColumns);
 		fh.put("indexType", indexType);
-		out.writeObject(fh);
+		//write the new attribut older versions will simply ignore it
+		fh.putBoolean("isUniqueWithDuplicateNulls", 
+                                        isUniqueWithDuplicateNulls);
+        out.writeObject(fh);
 	}
 
 	/* TypedFormat interface */
@@ -281,10 +319,11 @@
 			** elements (this is hardest, so save for last)
 			*/
 			if ((id.isUnique == this.isUnique) &&
+				(id.isUnique == this.isUnique) &&
 				(id.baseColumnPositions.length ==
 										this.baseColumnPositions.length) &&
 				(id.numberOfOrderedColumns == this.numberOfOrderedColumns) &&
-				(id.indexType.equals(this.indexType)))
+					(id.indexType.equals(this.indexType)))
 			{
 				/*
 				** Everything but array elements known to be true -

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/RegisteredFormatIds.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/RegisteredFormatIds.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/RegisteredFormatIds.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/RegisteredFormatIds.java Tue Mar  4 09:38:48 2008
@@ -527,9 +527,10 @@
         /* 463 */   "org.apache.derby.impl.sql.catalog.CoreDDFinderClassInfo",
         /* 464 */   "org.apache.derby.iapi.types.SqlXmlUtil",        
 		/* 465 */   "org.apache.derby.impl.store.raw.data.CompressSpacePageOperation",
-        /* 466 */   "org.apache.derby.impl.store.access.btree.index.B2I",
+        /* 466 */   "org.apache.derby.impl.store.access.btree.index.B2I_10_3",
         /* 467 */   "org.apache.derby.impl.store.access.heap.Heap",
-        /* 468 */       "org.apache.derby.iapi.types.DTSClassInfo",
-        /* 469 */       "org.apache.derby.catalog.types.RowMultiSetImpl"
+        /* 468 */   "org.apache.derby.iapi.types.DTSClassInfo",
+        /* 469 */   "org.apache.derby.catalog.types.RowMultiSetImpl",
+        /* 470 */   "org.apache.derby.impl.store.access.btree.index.B2I",
 };
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/StoredFormatIds.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/StoredFormatIds.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/StoredFormatIds.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/StoredFormatIds.java Tue Mar  4 09:38:48 2008
@@ -773,6 +773,7 @@
      */
     static public final int INDEX_DESCRIPTOR_IMPL_V02_ID =
             (MIN_ID_2 + 387);
+    
 
     /**
         class org.apache.derby.iapi.types.TinyintTypeId
@@ -1655,6 +1656,8 @@
     public static final int ACCESS_HEAP_V3_ID =
             (MIN_ID_2 + 467);
 
+    public static final int ACCESS_B2I_V5_ID = 
+            (MIN_ID_2 + 470);
     /******************************************************************
     **
     ** PropertyConglomerate
@@ -1892,7 +1895,7 @@
      * Make sure this is updated when a new module is added
      */
     public static final int MAX_ID_2 =
-            (MIN_ID_2 + 469);
+            (MIN_ID_2 + 470);
 
     // DO NOT USE 4 BYTE IDS ANYMORE
     static public final int MAX_ID_4 =

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ConglomerateDescriptor.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ConglomerateDescriptor.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ConglomerateDescriptor.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/ConglomerateDescriptor.java Tue Mar  4 09:38:48 2008
@@ -208,6 +208,7 @@
 		name = newName;
 	}
 
+
 	/**
 	 * Gets the index row generator for this conglomerate, null if the
 	 * conglomerate is not an index.
@@ -218,6 +219,7 @@
 	{
 		return indexRowGenerator;
 	}
+
 
 	/**
 	 * Set the column names for this conglomerate descriptor.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/IndexRowGenerator.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/IndexRowGenerator.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/IndexRowGenerator.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/IndexRowGenerator.java Tue Mar  4 09:38:48 2008
@@ -70,8 +70,48 @@
 								boolean[] isAscending,
 								int numberOfOrderedColumns)
 	{
+		id = new IndexDescriptorImpl(
+                        indexType,
+                        isUnique, //default uniqueWithDuplicateNulls to false
+                        false,
+                        baseColumnPositions,
+                        isAscending,
+                        numberOfOrderedColumns);
+
+		if (SanityManager.DEBUG)
+		{
+			SanityManager.ASSERT(baseColumnPositions != null,
+				"baseColumnPositions are null");
+		}
+	}
+        
+    /**
+     * Constructor for an IndexRowGeneratorImpl
+     * 
+     * @param indexType		The type of index
+     * @param isUnique		True means the index is unique
+     * @param isUniqueWithDuplicateNulls means the index is almost unique
+     *                              i.e. unique only for non null keys
+     * @param baseColumnPositions	An array of column positions in the base
+     * 								table.  Each index column corresponds to a
+     * 								column position in the base table.
+     * @param isAscending	An array of booleans telling asc/desc on each
+     * 						column.
+     * @param numberOfOrderedColumns	In the future, it will be possible
+     * 									to store non-ordered columns in an
+     * 									index.  These will be useful for
+     * 									covered queries.
+     */
+	public IndexRowGenerator(String indexType,
+								boolean isUnique,
+								boolean isUniqueWithDuplicateNulls,
+								int[] baseColumnPositions,
+								boolean[] isAscending,
+								int numberOfOrderedColumns)
+	{
 		id = new IndexDescriptorImpl(indexType,
 									isUnique,
+									isUniqueWithDuplicateNulls,
 									baseColumnPositions,
 									isAscending,
 									numberOfOrderedColumns);
@@ -229,6 +269,13 @@
 	{
 	}
 
+	/**
+     * @see IndexDescriptor#isisUniqueWithDuplicateNulls
+     */
+	public boolean isUniqueWithDuplicateNulls()
+	{
+		return id.isUniqueWithDuplicateNulls();
+	}
 	/** @see IndexDescriptor#isUnique */
 	public boolean isUnique()
 	{

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/AccessFactoryGlobals.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/AccessFactoryGlobals.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/AccessFactoryGlobals.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/AccessFactoryGlobals.java Tue Mar  4 09:38:48 2008
@@ -71,6 +71,8 @@
 
 	public static final String SORT_EXTERNAL = "sort external";
 	public static final String SORT_INTERNAL = "sort internal";
+    public static final String SORT_UNIQUEWITHDUPLICATENULLS_EXTERNAL 
+                                    = "sort almost unique external";
 
 	public static final String NESTED_READONLY_USER_TRANS = "nestedReadOnlyUserTransaction";
 	public static final String NESTED_UPDATE_USER_TRANS = "nestedUpdateUserTransaction";

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/RawStoreFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/RawStoreFactory.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/RawStoreFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/RawStoreFactory.java Tue Mar  4 09:38:48 2008
@@ -108,6 +108,9 @@
 
 	/** Derby Store Minor Version (3) **/
 	public static final int DERBY_STORE_MINOR_VERSION_3    = 3;
+        
+	/** Derby Store Minor Version (4) **/
+	public static final int DERBY_STORE_MINOR_VERSION_4    = 4;
 
 	/** Derby 10 Store Major version */
 	public static final int DERBY_STORE_MAJOR_VERSION_10   = 10;

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=633560&r1=633559&r2=633560&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 Tue Mar  4 09:38:48 2008
@@ -7553,15 +7553,33 @@
 
 		boolean[] isAscending = new boolean[baseColumnPositions.length];
 		for (int i = 0; i < baseColumnPositions.length; i++)
-			isAscending[i] = true;
+			isAscending[i]        = true;
+
+        IndexRowGenerator irg = null;
+
+        if (softwareVersion.checkVersion(
+                DataDictionary.DD_VERSION_DERBY_10_4,null)) 
+        {
+            irg = new IndexRowGenerator(
+                "BTREE", ti.isIndexUnique(indexNumber),
+                false,
+                baseColumnPositions,
+                isAscending,
+                baseColumnPositions.length);
+        }
+        else 
+        {
+            //older version of Data Disctionary
+            //use old constructor
+            irg = new IndexRowGenerator (
+                "BTREE", ti.isIndexUnique(indexNumber),
+                baseColumnPositions,
+                isAscending,
+                baseColumnPositions.length);
+        }
 
 		// For now, assume that all index columns are ordered columns
-		ti.setIndexRowGenerator(indexNumber, 
-								new IndexRowGenerator(
-											"BTREE", ti.isIndexUnique(indexNumber),
-											baseColumnPositions,
-											isAscending,
-											baseColumnPositions.length));
+		ti.setIndexRowGenerator(indexNumber, irg);
 	}
 
 	/**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateIndexNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateIndexNode.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateIndexNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateIndexNode.java Tue Mar  4 09:38:48 2008
@@ -288,19 +288,20 @@
         }
 
 
-		return	getGenericConstantActionFactory().getCreateIndexConstantAction(               
-                false, // not for CREATE TABLE
-                unique,
-											  indexType,
-											  sd.getSchemaName(),
-											  indexName.getTableName(),
-											  tableName.getTableName(),
-											  td.getUUID(),
-											  columnNames,
-											  isAscending,
-											  false,
-											  null,
-											  properties);
+		return getGenericConstantActionFactory().getCreateIndexConstantAction(
+                    false, // not for CREATE TABLE
+                    unique,
+                    false, //its not a UniqueWithDuplicateNulls Index
+                    indexType,
+                    sd.getSchemaName(),
+                    indexName.getTableName(),
+                    tableName.getTableName(),
+                    td.getUUID(),
+                    columnNames,
+                    isAscending,
+                    false,
+                    null,
+                    properties);
 	}
 
 	/**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableElementList.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableElementList.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableElementList.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableElementList.java Tue Mar  4 09:38:48 2008
@@ -322,9 +322,16 @@
             }
             else if (cdn.hasUniqueKeyConstraint())
             {
-                // for UNIQUE, check that columns are unique and NOT NULL
+                // for UNIQUE, check that columns are unique
                 verifyUniqueColumnList(ddlStmt, cdn);
-                checkForNullColumns(cdn, td);
+
+                // unique constraints on nullable columns added in 10.4, 
+                // disallow until database hard upgraded at least to 10.4.
+                if (!dd.checkVersion(
+                        DataDictionary.DD_VERSION_DERBY_10_4, null))
+                {
+                    checkForNullColumns(cdn, td);
+                }
             }
             else if (cdn.hasForeignKeyConstraint())
             {
@@ -672,12 +679,47 @@
 			 */
 			if (constraintDN.requiresBackingIndex())
 			{
-				indexAction = genIndexAction(
-                        forCreateTable,
-                        constraintDN.requiresUniqueIndex(),
-											 null, constraintDN, 
-											 columnNames, true, tableSd, tableName,
-											 constraintType, dd);
+                // implement unique constraints using a unique backing index 
+                // unless it is soft upgrade in version before 10.4, or if 
+                // constraint contains no nullable columns.  In 10.4 use 
+                // "unique with duplicate null" backing index for constraints 
+                // that contain at least one nullable column.
+
+				if (constraintDN.constraintType ==
+					DataDictionary.UNIQUE_CONSTRAINT && 
+					(dd.checkVersion(
+                         DataDictionary.DD_VERSION_DERBY_10_4, null))) 
+                {
+                    boolean contains_nullable_columns = 
+                        areColumnsNullable(constraintDN, td);
+
+                    // if all the columns are non nullable, continue to use
+                    // a unique backing index.
+                    boolean unique = 
+                        !contains_nullable_columns;
+
+                    // Only use a "unique with duplicate nulls" backing index
+                    // for constraints with nullable columns.
+                    boolean uniqueWithDuplicateNulls = 
+                        contains_nullable_columns;
+
+					indexAction = genIndexAction(
+						forCreateTable,
+						unique,
+                        uniqueWithDuplicateNulls,
+						null, constraintDN,
+						columnNames, true, tableSd, tableName,
+						constraintType, dd);
+				} 
+                else 
+                {
+					indexAction = genIndexAction(
+						forCreateTable,
+						constraintDN.requiresUniqueIndex(), false,
+						null, constraintDN,
+						columnNames, true, tableSd, tableName,
+						constraintType, dd);
+				}
 			}
 
 			if (constraintType == DataDictionary.DROP_CONSTRAINT)
@@ -772,54 +814,88 @@
 		return true;
 	}
 
+    /**
+     * utility to generated the call to create the index.
+     * <p>
+     *
+     *
+     * @param forCreateTable                Executed as part of a CREATE TABLE
+     * @param isUnique		                True means it will be a unique index
+     * @param isUniqueWithDuplicateNulls    True means index check and disallow
+     *                                      any duplicate key if key has no 
+     *                                      column with a null value.  If any 
+     *                                      column in the key has a null value,
+     *                                      no checking is done and insert will
+     *                                      always succeed.
+     * @param indexName	                    The type of index (BTREE, for 
+     *                                      example)
+     * @param cdn
+     * @param columnNames	                Names of the columns in the index,
+     *                                      in order.
+     * @param isConstraint	                TRUE if index is backing up a 
+     *                                      constraint, else FALSE.
+     * @param sd
+     * @param tableName	                    Name of table the index will be on
+     * @param constraintType
+     * @param dd
+     **/
 	private IndexConstantAction genIndexAction(
-            boolean forCreateTable,
-										boolean	isUnique,
-										String indexName,
-										ConstraintDefinitionNode cdn,
-										String[] columnNames,
-										boolean isConstraint,
-										SchemaDescriptor sd,
-										String tableName,
-										int constraintType,
-										DataDictionary dd)
+    boolean                     forCreateTable,
+    boolean                     isUnique,
+    boolean                     isUniqueWithDuplicateNulls,
+    String                      indexName,
+    ConstraintDefinitionNode    cdn,
+    String[]                    columnNames,
+    boolean                     isConstraint,
+    SchemaDescriptor            sd,
+    String                      tableName,
+    int                         constraintType,
+    DataDictionary              dd)
 		throws StandardException
 	{
-		if ( indexName == null ) { indexName = cdn.getBackingIndexName(dd); }
+		if (indexName == null) 
+        { 
+            indexName = cdn.getBackingIndexName(dd); 
+        }
 
 		if (constraintType == DataDictionary.DROP_CONSTRAINT)
 		{
             if (SanityManager.DEBUG)
             {
                 if (forCreateTable)
-                    SanityManager.THROWASSERT("DROP INDEX with forCreateTable true");
+                    SanityManager.THROWASSERT(
+                        "DROP INDEX with forCreateTable true");
             }
-			return	getGenericConstantActionFactory().getDropIndexConstantAction(
-									  null,
-									  indexName,
-									  tableName,
-									  sd.getSchemaName(),
-									  td.getUUID(),
-									  td.getHeapConglomerateId());
+
+			return getGenericConstantActionFactory().getDropIndexConstantAction(
+                      null,
+                      indexName,
+                      tableName,
+                      sd.getSchemaName(),
+                      td.getUUID(),
+                      td.getHeapConglomerateId());
 		}
 		else
 		{
 			boolean[]	isAscending = new boolean[columnNames.length];
+
 			for (int i = 0; i < isAscending.length; i++)
 				isAscending[i] = true;
+
 			return	getGenericConstantActionFactory().getCreateIndexConstantAction(
-                    forCreateTable,
-									isUnique,
-									"BTREE", // indexType
-									sd.getSchemaName(),
-									indexName,
-									tableName,
-									((td != null) ? td.getUUID() : (UUID) null),
-									columnNames,
-									isAscending,
-									isConstraint,
-									cdn.getBackingIndexUUID(),
-									cdn.getProperties());
+                    forCreateTable, 
+                    isUnique, 
+                    isUniqueWithDuplicateNulls,
+                    "BTREE", // indexType
+                    sd.getSchemaName(),
+                    indexName,
+                    tableName,
+                    ((td != null) ? td.getUUID() : (UUID) null),
+                    columnNames,
+                    isAscending,
+                    isConstraint,
+                    cdn.getBackingIndexUUID(),
+                    cdn.getProperties());
 		}
 	}
 
@@ -940,6 +1016,40 @@
         }
 	}
 
+    /**
+     * Checks if any of the columns in the constraint can be null.
+     *
+     * @param cdn Constraint node
+     * @param td tabe descriptor of the target table
+     *
+     * @return true if any of the column can be null false other wise
+     */
+    private boolean areColumnsNullable (
+    ConstraintDefinitionNode    cdn, 
+    TableDescriptor             td) 
+    {
+        ResultColumnList rcl = cdn.getColumnList();
+        int rclSize = rcl.size();
+        for (int index = 0; index < rclSize; index++)
+        {
+            String colName = ((ResultColumn) rcl.elementAt(index)).getName();
+            DataTypeDescriptor dtd;
+            if (td == null)
+            {
+                dtd = getColumnDataTypeDescriptor(colName);
+            }
+            else
+            {
+                dtd = getColumnDataTypeDescriptor(colName, td);
+            }
+            // todo dtd may be null if the column does not exist, we should check that first
+            if (dtd != null && dtd.isNullable())
+            {
+                return true;
+            }
+        }
+        return false;
+    }
 
     private void checkForNullColumns(ConstraintDefinitionNode cdn, TableDescriptor td) throws StandardException
     {

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateIndexConstantAction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateIndexConstantAction.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateIndexConstantAction.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CreateIndexConstantAction.java Tue Mar  4 09:38:48 2008
@@ -47,6 +47,7 @@
 import org.apache.derby.iapi.sql.execute.ConstantAction;
 import org.apache.derby.iapi.sql.execute.ExecIndexRow;
 import org.apache.derby.iapi.sql.execute.ExecRow;
+import org.apache.derby.iapi.store.access.AccessFactoryGlobals;
 import org.apache.derby.iapi.store.access.ColumnOrdering;
 import org.apache.derby.iapi.store.access.ConglomerateController;
 import org.apache.derby.iapi.store.access.GroupFetchScanController;
@@ -76,6 +77,7 @@
     private final boolean forCreateTable;
 
 	private boolean			unique;
+	private boolean			uniqueWithDuplicateNulls;
 	private String			indexType;
 	private String[]		columnNames;
 	private boolean[]		isAscending;
@@ -108,38 +110,52 @@
 
 	// CONSTRUCTORS
 	/**
-	 *	Make the ConstantAction to create an index.
-	 *
-     *  @param forCreateTable Being executed within a CREATE TABLE statement
-	 *  @param unique		True means it will be a unique index
-	 *  @param indexType	The type of index (BTREE, for example)
-	 *  @param schemaName	the schema that table (and index) lives in.
-	 *  @param indexName	Name of the index
-	 *  @param tableName	Name of table the index will be on
-	 *  @param tableId		UUID of table
-	 *  @param columnNames	Names of the columns in the index, in order
-	 *	@param isAscending	Array of booleans telling asc/desc on each column
-	 *  @param isConstraint	TRUE if index is backing up a constraint, else FALSE
-	 *  @param conglomerateUUID	ID of conglomerate
-	 *  @param properties	The optional properties list associated with the index.
-	 */
+     * 	Make the ConstantAction to create an index.
+     * 
+     * @param forCreateTable                Being executed within a CREATE TABLE
+     *                                      statement
+     * @param unique		                True means it will be a unique index
+     * @param isUniqueWithDuplicateNulls    True means index check and disallow
+     *                                      any duplicate key if key has no 
+     *                                      column with a null value.  If any 
+     *                                      column in the key has a null value,
+     *                                      no checking is done and insert will
+     *                                      always succeed.
+     * @param indexType	                    type of index (BTREE, for example)
+     * @param schemaName	                schema that table (and index) 
+     *                                      lives in.
+     * @param indexName	                    Name of the index
+     * @param tableName	                    Name of table the index will be on
+     * @param tableId		                UUID of table
+     * @param columnNames	                Names of the columns in the index, 
+     *                                      in order
+     * @param isAscending	                Array of booleans telling asc/desc 
+     *                                      on each column
+     * @param isConstraint	                TRUE if index is backing up a 
+     *                                      constraint, else FALSE
+     * @param conglomerateUUID	            ID of conglomerate
+     * @param properties	                The optional properties list 
+     *                                      associated with the index.
+     */
 	CreateIndexConstantAction(
-            boolean forCreateTable,
-								boolean			unique,
-								String			indexType,
-								String			schemaName,
-								String			indexName,
-								String			tableName,
-								UUID			tableId,
-								String[]		columnNames,
-								boolean[]		isAscending,
-								boolean			isConstraint,
-								UUID			conglomerateUUID,
-								Properties		properties)
+            boolean         forCreateTable,
+            boolean			unique,
+            boolean			uniqueWithDuplicateNulls,
+            String			indexType,
+            String			schemaName,
+            String			indexName,
+            String			tableName,
+            UUID			tableId,
+            String[]		columnNames,
+            boolean[]		isAscending,
+            boolean			isConstraint,
+            UUID			conglomerateUUID,
+            Properties		properties)
 	{
 		super(tableId, indexName, tableName, schemaName);
         this.forCreateTable = forCreateTable;
 		this.unique = unique;
+		this.uniqueWithDuplicateNulls = uniqueWithDuplicateNulls;
 		this.indexType = indexType;
 		this.columnNames = columnNames;
 		this.isAscending = isAscending;
@@ -214,7 +230,7 @@
 			}
 		}
 	}
-
+        
 	///////////////////////////////////////////////
 	//
 	// OBJECT SHADOWS
@@ -535,6 +551,25 @@
 		// Tell it the conglomerate id of the base table
 		indexProperties.put("baseConglomerateId",
 							Long.toString(td.getHeapConglomerateId()));
+        
+		if (uniqueWithDuplicateNulls) 
+        {
+			if (lcc.getDataDictionary().checkVersion(
+				DataDictionary.DD_VERSION_DERBY_10_4, null)) 
+            {
+				indexProperties.put(
+                    "uniqueWithDuplicateNulls", Boolean.toString(true));
+			}
+			else 
+            {
+				// for lower version of DD there is no unique with nulls 
+                // index creating a unique index instead.
+				if (uniqueWithDuplicateNulls) 
+                {
+					unique = true;
+				}
+			}
+		}
 
 		// All indexes are unique because they contain the RowLocation.
 		// The number of uniqueness columns must include the RowLocation
@@ -543,7 +578,6 @@
 					Integer.toString(unique ? baseColumnPositions.length :
 												baseColumnPositions.length + 1)
 							);
-
 		// By convention, the row location column is the last column
 		indexProperties.put("rowLocationColumn",
 							Integer.toString(baseColumnPositions.length));
@@ -555,10 +589,26 @@
 		// For now, assume that all index columns are ordered columns
 		if (! shareExisting)
 		{
-			indexRowGenerator = new IndexRowGenerator(indexType, unique,
-													baseColumnPositions,
-													isAscending,
-													baseColumnPositions.length);
+			if (lcc.getDataDictionary().checkVersion(
+					DataDictionary.DD_VERSION_DERBY_10_4, null)) 
+            {
+                indexRowGenerator = new IndexRowGenerator(
+                                            indexType, 
+                                            unique, 
+                                            uniqueWithDuplicateNulls,
+                                            baseColumnPositions,
+                                            isAscending,
+                                            baseColumnPositions.length);
+			}
+			else 
+            {
+				indexRowGenerator = new IndexRowGenerator(
+                                            indexType, 
+                                            unique,
+                                            baseColumnPositions,
+                                            isAscending,
+                                            baseColumnPositions.length);
+			}
 		}
 
 		/* Now add the rows from the base table to the conglomerate.
@@ -679,9 +729,8 @@
 			 */
 			int numColumnOrderings;
 			SortObserver sortObserver = null;
-			if (unique)
+			if (unique || uniqueWithDuplicateNulls)
 			{
-				numColumnOrderings = baseColumnPositions.length;
 				// if the index is a constraint, use constraintname in possible error messagge
 				String indexOrConstraintName = indexName;
 				if  (conglomerateUUID != null)
@@ -694,11 +743,40 @@
 						indexOrConstraintName = conDesc.getConstraintName();
 					}
 				}
-				sortObserver = new UniqueIndexSortObserver(true, isConstraint, 
-														   indexOrConstraintName,
-														   indexTemplateRow,
-														   true,
-														   td.getName());
+
+				if (unique) 
+				{
+                    numColumnOrderings = baseColumnPositions.length;
+
+					sortObserver = 
+                        new UniqueIndexSortObserver(
+                                true, 
+                                isConstraint, 
+                                indexOrConstraintName,
+                                indexTemplateRow,
+                                true,
+                                td.getName());
+				}
+				else 
+                {
+                    // unique with duplicate nulls allowed.
+
+					numColumnOrderings = baseColumnPositions.length + 1;
+
+					properties = new Properties();
+					properties.put(
+                        AccessFactoryGlobals.IMPL_TYPE, 
+                        AccessFactoryGlobals.SORT_UNIQUEWITHDUPLICATENULLS_EXTERNAL);
+					//use sort operator which treats nulls unequal
+					sortObserver = 
+                        new UniqueWithDuplicateNullsIndexSortObserver(
+                                true, 
+                                isConstraint, 
+                                indexOrConstraintName,
+                                indexTemplateRow,
+                                true,
+                                td.getName());
+				}
 			}
 			else
 			{
@@ -719,7 +797,7 @@
 			}
 
 			// create the sorter
-			sortId = tc.createSort((Properties)null, 
+			sortId = tc.createSort((Properties)properties, 
 					indexTemplateRow.getRowArrayClone(),
 					order,
 					sortObserver,

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericConstantActionFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericConstantActionFactory.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericConstantActionFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericConstantActionFactory.java Tue Mar  4 09:38:48 2008
@@ -192,25 +192,32 @@
 
 
 	/**
-	 *	Make the ConstantAction for a CREATE INDEX statement.
-	 *
-     *  @param forCreateTable Executed as part of a CREATE TABLE
-	 *  @param unique		True means it will be a unique index
-	 *  @param indexType	The type of index (BTREE, for example)
-	 *  @param schemaName			the schema that table (and index) lives in.
-	 *  @param indexName	Name of the index
-	 *  @param tableName	Name of table the index will be on
-	 *	@param tableId		UUID of table.
-	 *  @param columnNames	Names of the columns in the index, in order
-	 *  @param isAscending	Array of booleans telling asc/desc on each column
-	 *  @param isConstraint	TRUE if index is backing up a constraint, else FALSE
-	 *  @param conglomerateUUID	ID of conglomerate
-	 *  @param properties	The optional properties list associated with the index.
-	 */
+     * 	Make the ConstantAction for a CREATE INDEX statement.
+     * 
+     * @param forCreateTable Executed as part of a CREATE TABLE
+     * @param unique		True means it will be a unique index
+     * @param uniqueWithDuplicateNulls  True means index check and disallow
+     *                                  any duplicate key if key has no 
+     *                                  column with a null value.  If any 
+     *                                  column in the key has a null value,
+     *                                  no checking is done and insert will
+     *                                  always succeed.
+     * @param indexType	The type of index (BTREE, for example)
+     * @param schemaName			the schema that table (and index) lives in.
+     * @param indexName	Name of the index
+     * @param tableName	Name of table the index will be on
+     * @param tableId		UUID of table.
+     * @param columnNames	Names of the columns in the index, in order
+     * @param isAscending	Array of booleans telling asc/desc on each column
+     * @param isConstraint	TRUE if index is backing up a constraint, else FALSE
+     * @param conglomerateUUID	ID of conglomerate
+     * @param properties	The optional properties list associated with the index.
+     */
 	public	CreateIndexConstantAction	getCreateIndexConstantAction
 	(
         boolean forCreateTable,
 		boolean			unique,
+		boolean			uniqueWithDuplicateNulls,
 		String			indexType,
 		String			schemaName,
 		String			indexName,
@@ -224,7 +231,8 @@
     )
 	{
 		return	new CreateIndexConstantAction
-			( forCreateTable, unique, indexType, schemaName, indexName, tableName, tableId,
+			( forCreateTable, unique, uniqueWithDuplicateNulls, indexType, 
+				schemaName, indexName, tableName, tableId,
 			  columnNames, isAscending, isConstraint,
 			  conglomerateUUID, properties );
 	}

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/IndexChanger.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/IndexChanger.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/IndexChanger.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/IndexChanger.java Tue Mar  4 09:38:48 2008
@@ -606,8 +606,8 @@
 		 throws StandardException
 	{
 		setOurIndexRow(newRow, baseRowLocation);
-
-		if (irg.isUnique())
+		//defer inserts if its on unique or UniqueWhereNotNull index
+		if (irg.isUnique() || irg.isUniqueWithDuplicateNulls())
 		{
 			doDeferredInsert();
 		}

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UniqueWithDuplicateNullsIndexSortObserver.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UniqueWithDuplicateNullsIndexSortObserver.java?rev=633560&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UniqueWithDuplicateNullsIndexSortObserver.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UniqueWithDuplicateNullsIndexSortObserver.java Tue Mar  4 09:38:48 2008
@@ -0,0 +1,87 @@
+/*
+ 
+   Derby - Class org.apache.derby.impl.sql.execute.UniqueWithDuplicateNullsIndexSortObserver
+ 
+   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.execute;
+
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.sql.execute.ExecRow;
+import org.apache.derby.iapi.types.DataValueDescriptor;
+
+/**
+ * UniqueWithDuplicateNullsIndexSortObserver is implementation of BasicSortObserver for
+ * eliminating non null duplicates from the backing index of unique constraint.
+ * This class is implemented to check for special case of distinct sorting where
+ * duplicate keys are allowed only if there is a null in the key part.
+ */
+public class UniqueWithDuplicateNullsIndexSortObserver extends BasicSortObserver {
+    
+    private boolean		isConstraint;
+    private String		indexOrConstraintName;
+    private String 		tableName;
+    
+    /**
+     * Constructs an object of UniqueWithDuplicateNullsIndexSortObserver
+     * 
+     * 
+     * 
+     * @param doClone If true, then rows that are retained
+     * 		by the sorter will be cloned.  This is needed
+     * 		if language is reusing row wrappers.
+     * @param isConstraint is this part of a constraint
+     * @param indexOrConstraintName name of index of constraint
+     * @param distinct	If true, toss out duplicates.
+     * 		Otherwise, retain them.
+     * @param execRow	ExecRow to use as source of clone for store.
+     * @param reuseWrappers	Whether or not we can reuse the wrappers
+     * @param tableName name of the table
+     */
+    public UniqueWithDuplicateNullsIndexSortObserver(boolean doClone, boolean isConstraint,
+            String indexOrConstraintName, ExecRow execRow,
+            boolean reuseWrappers, String tableName) {
+        super(doClone, false, execRow, reuseWrappers);
+        this.isConstraint = isConstraint;
+        this.indexOrConstraintName = indexOrConstraintName;
+        this.tableName = tableName;
+    }
+    
+    /**
+     * Methods to check if the duplicate key can be inserted or not. It throws 
+     * exception if the duplicates has no null part in the key. 
+     * @param in new key
+     * @param dup the new key is duplicate of this key
+     * @returns DVD [] if there is at least one null in
+     * the key else thorws StandardException
+     * @throws StandardException is the duplicate key has all non null parts
+     */
+    public DataValueDescriptor[] insertDuplicateKey(DataValueDescriptor[] in,
+            DataValueDescriptor[] dup) throws StandardException {
+        for (int i = 0; i < in.length; i++) {
+            if (in [i].isNull()) {
+                return super.insertDuplicateKey(in, dup);
+            }
+        }
+        StandardException se = null;
+        se = StandardException.newException(
+                SQLState.LANG_DUPLICATE_KEY_CONSTRAINT,
+                indexOrConstraintName, tableName);
+        throw se;
+    }
+}

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

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTree.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTree.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTree.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTree.java Tue Mar  4 09:38:48 2008
@@ -112,6 +112,11 @@
     public static final String PROPERTY_NUNIQUECOLUMNS  = "nUniqueColumns";
     public static final String PROPERTY_PARENTLINKS     = "maintainParentLinks";
 
+    //property key to indicate if the index will allow duplicate nulls, but
+    //otherwise insure unique keys
+    public static final String PROPERTY_UNIQUE_WITH_DUPLICATE_NULLS 
+                                                    = "uniqueWithDuplicateNulls";
+
 
 
     /**************************************************************************
@@ -147,6 +152,15 @@
 	**/
 	boolean maintainParentLinks;
 
+	/**
+	Attribute to indicate the index allows duplicate only in
+	case of keys with no part null.  This attribute has no effect if unique
+    is true. If unique is false and isUniqueWithDuplicateNulls is set 
+    to true the index will allow duplicates of any key with at least one
+    column null, but for non null keys will act like a unique index.
+	**/
+	boolean uniqueWithDuplicateNulls = false;
+
     /**
     Maximum rows per page to place on a btree leaf or nonleaf page.  Used
     by testing to finely control split points.  Only changed for debugging
@@ -302,6 +316,27 @@
     {
         return(nKeyFields != nUniqueColumns);
     }
+    
+
+    /**
+     * Set if the index is unique only for non null keys
+     * 
+     * @param uniqueWithDuplicateNulls true if the index will be unique only for
+     *                                 non null keys
+     */
+    public void setUniqueWithDuplicateNulls (boolean uniqueWithDuplicateNulls) 
+    {
+        this.uniqueWithDuplicateNulls = uniqueWithDuplicateNulls;
+    }
+
+    /**
+     * Returns if the index type is uniqueWithDuplicateNulls.
+     * @return is index type is uniqueWithDuplicateNulls
+     */
+    public boolean isUniqueWithDuplicateNulls()
+    {
+        return uniqueWithDuplicateNulls;
+    }
 
     /**************************************************************************
      * Public Methods of Conglomerate Interface:
@@ -408,8 +443,9 @@
         }
 
         // Check input arguments
-        allowDuplicates = (Boolean.valueOf(
-            properties.getProperty(PROPERTY_ALLOWDUPLICATES, "false"))).booleanValue();
+        allowDuplicates = 
+            (Boolean.valueOf(properties.getProperty(
+                PROPERTY_ALLOWDUPLICATES, "false"))).booleanValue();
 
         result_string = properties.getProperty(PROPERTY_NKEYFIELDS);
         if (result_string == null)
@@ -433,6 +469,11 @@
         {
             nUniqueColumns = Integer.parseInt(result_string);
         }
+        
+        result_string = 
+            properties.getProperty(
+                PROPERTY_UNIQUE_WITH_DUPLICATE_NULLS, "false");
+        uniqueWithDuplicateNulls = new Boolean (result_string).booleanValue();
 
 
         if (SanityManager.DEBUG)

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeController.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeController.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeController.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeController.java Tue Mar  4 09:38:48 2008
@@ -42,6 +42,7 @@
 import org.apache.derby.iapi.store.raw.FetchDescriptor;
 import org.apache.derby.iapi.store.raw.LockingPolicy;
 import org.apache.derby.iapi.store.raw.Page;
+import org.apache.derby.iapi.store.raw.RecordHandle;
 import org.apache.derby.iapi.store.raw.Transaction;
 
 import org.apache.derby.iapi.types.DataValueDescriptor;
@@ -74,6 +75,11 @@
      * has already been gotten when the row was inserted into the base table.
      **/
     boolean get_insert_row_lock;
+    
+    //constants for the status of dupicate checking
+    private static final int NO_MATCH = 0;
+    private static final int MATCH_FOUND = 1;
+    private static final int RESCAN_REQUIRED = 2;
 
     /* Constructors: */
 
@@ -350,6 +356,207 @@
 
         return(new_leaf_pageno);
     }
+    
+    /**
+     * Compares the oldrow with the one at 'slot' or the one left to it. 
+     * If the slot is first slot it will move to the left sibiling of 
+     * the 'leaf' and will compare with the record from the last slot.
+     * @param slot slot number to start with
+     * @param leaf LeafControlRow of the current page
+     * @param rows DataValueDescriptot array to fill it with fetched values
+     * @return  0 if no duplicate
+     *          1 if duplicate 
+     *          2 if rescan required
+     * @throws StandardException
+     */
+    private int comparePreviousRecord (int slot, 
+                                    LeafControlRow  leaf, 
+                                    DataValueDescriptor [] rows,
+                                    DataValueDescriptor [] oldRows) 
+                                        throws StandardException {
+        RecordHandle rh = null;
+        boolean newLeaf = false;
+        LeafControlRow originalLeaf = leaf;
+        while (leaf != null) {
+            if (slot == 0) {
+                try {
+                    LeafControlRow oldLeaf = leaf;
+                    //slot is pointing before the first slot
+                    //get left sibiling
+                    leaf = (LeafControlRow) leaf.getLeftSibling(this);
+                    //no left sibiling
+                    if (leaf == null)
+                        return NO_MATCH;
+                    //set the slot to last slot number
+                    slot = leaf.page.recordCount() - 1;
+                    if (newLeaf)
+                        oldLeaf.release();
+                    newLeaf = true;
+                } catch (WaitError we) {
+                    throw StandardException.plainWrapException(we);
+                }
+            }
+            rh = leaf.page.fetchFromSlot(null, slot, rows, null, true);
+            if (rh != null) {
+                int ret = compareRowsForInsert(rows, oldRows, leaf, slot);
+                //release the page if required
+                if (ret == RESCAN_REQUIRED && newLeaf) {
+                    originalLeaf.release();
+                }
+                if (ret != RESCAN_REQUIRED && newLeaf) {
+                    leaf.release();
+                }
+                return ret;
+            }
+            slot++;
+        }
+        return NO_MATCH;
+    }
+    
+    /**
+     * Compares the new record with the one at slot or the one 
+     * right to it. If the slot is last slot in the page it will move to 
+     * the right to sibling of the leaf and will compare with the record 
+     * from the last slot. 
+     * @param slot slot number to start with
+     * @param leaf LeafControlRow of the current page
+     * @param rows DataValueDescriptot array to fill it with fetched values
+     * @return  0 if no duplicate
+     *          1 if duplicate 
+     *          2 if rescan required
+     * @throws StandardException
+     */
+    private int compareNextRecord (int slot, 
+                                    LeafControlRow  leaf, 
+                                    DataValueDescriptor [] rows,
+                                    DataValueDescriptor [] oldRows) 
+                                        throws StandardException {
+        RecordHandle rh = null;
+        boolean newLeaf = false;
+        LeafControlRow originalLeaf = leaf;
+        while (leaf != null) {
+            if (slot >= leaf.page.recordCount()) {
+                //slot is pointing to last slot
+                //get next sibling
+                LeafControlRow oldLeaf = leaf;
+                leaf = (LeafControlRow) leaf.getRightSibling(this);
+                if (newLeaf) {
+                    oldLeaf.release();
+                }
+                newLeaf = true;
+                //this was right most leaf
+                //no record at the right
+                if (leaf == null)
+                    return NO_MATCH;
+                //point slot to the first record of new leaf
+                slot = 1;
+            }
+            rh = leaf.page.fetchFromSlot(null, slot, rows, null, true);
+            if (rh != null) {
+                int ret =  compareRowsForInsert(rows, oldRows, leaf, slot);
+                if (ret == RESCAN_REQUIRED && newLeaf) {
+                    originalLeaf.release();
+                }
+                if (ret != RESCAN_REQUIRED && newLeaf) {
+                    leaf.release();
+                }
+                return ret;
+            }
+            slot++;
+        }
+        return NO_MATCH;
+    }
+    
+    /**
+     * Compares two row for insert. If the two rows are equal it checks if the 
+     * row in tree is deleted. If not MATCH_FOUND is returned. If the row is 
+     * deleted it tries to get a lock on that. If a lock is obtained without 
+     * waiting (ie without losing the latch) the row was deleted within the 
+     * same transaction and its safe to insert. NO_MATCH is returned in this 
+     * case. If latch is released while waiting for lock rescaning the tree 
+     * is required as the tree might have been rearanged by some other 
+     * transaction. RESCAN_REQUIRED is returned in this case.
+     * In case of NO_MATCH and MATCH_FOUND latch is also released.
+     * @param originalRow row from the tree
+     * @param newRow row to be inserted
+     * @param leaf leaf where originalRow resides
+     * @param slot slot where originalRow
+     * @return  0 if no duplicate
+     *          1 if duplicate 
+     *          2 if rescan required
+     */
+    private int compareRowsForInsert (DataValueDescriptor [] originalRow,
+                                      DataValueDescriptor [] newRow,
+                                      LeafControlRow leaf, int slot) 
+                                            throws StandardException {
+        for (int i = 0; i < originalRow.length - 1; i++) {
+            if (!originalRow [i].equals(newRow [i]))
+                return NO_MATCH;
+        }
+        //It might be a deleted record try getting a lock on it
+        DataValueDescriptor[] template = runtime_mem.get_template(getRawTran());
+        FetchDescriptor lock_fetch_desc = RowUtil.getFetchDescriptorConstant(
+                                                    template.length - 1);
+        RowLocation lock_row_loc = 
+            (RowLocation) scratch_template[scratch_template.length - 1];
+        boolean latch_released = !getLockingPolicy().lockNonScanRowOnPage(
+                this.getConglomerate(), leaf, slot, lock_fetch_desc,template, 
+                lock_row_loc, ConglomerateController.LOCK_UPD);
+        //if latch was released some other transaction was operating on this
+        //record and might have changed the tree by now
+        if (latch_released)
+            return RESCAN_REQUIRED;
+        //there is match check if its not deleted
+        if (!leaf.page.isDeletedAtSlot(slot)) {
+            //its a genuine match
+            return MATCH_FOUND;
+        }
+        //it is a deleted record within same transaction
+        //safe to insert
+        return NO_MATCH;
+    }
+    
+    /**
+     * Compares immidiate left and right records to check for duplicates.
+     * This methods compares new record (being inserted) with the record 
+     * in immidate left and right postion to see if its duplicate (only for
+     * almost unique index and for non null keys)
+     * @param rowToInsert row being inserted
+     * @param insert_slot slot where rowToInsert is being inserted
+     * @param targetleaf page where rowToInsert
+     * @return  0 if no duplicate
+     *          1 if duplicate 
+     *          2 if rescan required
+     * @throws StandardException
+     */
+    private int compareLeftAndRightSiblings (
+                            DataValueDescriptor[] rowToInsert, 
+                            int insert_slot, 
+                            LeafControlRow  targetleaf) throws StandardException {
+        //proceed only if almost unique index
+        if (this.getConglomerate().isUniqueWithDuplicateNulls()) {
+            int keyParts = rowToInsert.length - 1;
+            boolean hasnull = false;
+            for (int i = 0; i < keyParts; i++) {
+                //keys with null in it are unique
+                //no need to compare
+                if (rowToInsert [i].isNull()) {
+                    return NO_MATCH;
+                }
+            }
+            if (!hasnull) {
+                DataValueDescriptor index [] =  
+                        runtime_mem.get_template(getRawTran());
+                int ret = comparePreviousRecord(insert_slot - 1, 
+                        targetleaf, index, rowToInsert);
+                if (ret > 0) {
+                    return ret;                        
+                }
+                return compareNextRecord(insert_slot, targetleaf, index, rowToInsert);
+            }
+        }
+        return NO_MATCH;
+    }
 
 	/**
     Insert a row into the conglomerate.
@@ -600,7 +807,14 @@
                 // on the page returned by the search.
                 insert_slot = sp.resultSlot + 1;
                 result_slot = insert_slot + 1;
-
+                if (getConglomerate().isUniqueWithDuplicateNulls()) {
+                    int ret = compareLeftAndRightSiblings(rowToInsert, 
+                            insert_slot, targetleaf);
+                    if (ret == MATCH_FOUND)
+                        return ConglomerateController.ROWISDUPLICATE;
+                    if (ret == RESCAN_REQUIRED)
+                        continue;
+                }
                 // By default maxRowsPerPage is set to MAXINT, some tests
                 // set it small to cause splitting to happen quicker with
                 // less data.
@@ -630,7 +844,14 @@
 
                 // start splitting ...
             }
-
+            if (getConglomerate().isUniqueWithDuplicateNulls()) {
+                int ret = compareLeftAndRightSiblings(rowToInsert, 
+                        insert_slot, targetleaf);
+                if (ret == MATCH_FOUND)
+                    return ConglomerateController.ROWISDUPLICATE;
+                if (ret == RESCAN_REQUIRED)
+                    continue;
+            }
             
             // Create some space by splitting pages.
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I.java Tue Mar  4 09:38:48 2008
@@ -33,6 +33,8 @@
 
 import org.apache.derby.iapi.services.sanity.SanityManager;
 import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.store.raw.Page;
+import org.apache.derby.impl.store.access.btree.ControlRow;
 
 import org.apache.derby.impl.store.access.conglomerate.ConglomerateUtil;
 import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
@@ -143,8 +145,70 @@
  *            conglomerate id itself.  There exists a single B2IFactory which
  *            must be able to read all btree format id's.  
  *
+ *            This format was used for all Derby database B2I's in version 10.3.
+ *
+ * @upgrade   The format id of this object is currently always read from disk
+ *            as the first field of the conglomerate itself.  A bootstrap
+ *            problem exists as we don't know the format id of the B2I 
+ *            until we are in the "middle" of reading the B2I.  Thus the
+ *            base B2I implementation must be able to read and write 
+ *            all formats based on the reading the 
+ *            "format_of_this_conglomerate". 
+ *
+ *            soft upgrade to ACCESS_B2I_V5_ID:
+ *                read:
+ *                    old format is readable by current B2I implementation,
+ *                    with automatic in memory creation of default
+ *                    isUniqueWithDuplicateNulls value of false.
+ *                    No code other than readExternal and writeExternal need 
+ *                    know about old format.
+ *                write:
+ *                    will never write out new format id in soft upgrade mode.
+ *                    Code in readExternal and writeExternal handles writing
+ *                    correct version.  Code in the factory handles making
+ *                    sure new conglomerates use the B2I_v10_3 class
+ *                    that will write out old format info.
+ *
+ *            hard upgrade to ACCESS_B2I_V5_ID:
+ *                read:
+ *                    old format is readable by current B2I implementation,
+ *                    with automatic in memory creation of default
+ *                    isUniqueWithDuplicateNulls value of false.
+ *
+ *                write:
+ *                    Only "lazy" upgrade will happen.  New format will only
+ *                    get written for new conglomerate created after the 
+ *                    upgrade.  Old conglomerates continue to be handled the
+ *                    same as soft upgrade.
+ *
+ * @disk_layout 
+ *     format_of_this_conlgomerate(byte[])
+ *     containerid(long)
+ *     segmentid(int)
+ *     number_of_key_fields(int)
+ *     number_of_unique_columns(int)
+ *     allow_duplicates(boolean)
+ *     maintain_parent_links(boolean)
+ *     array_of_format_ids(byte[][])
+ *     baseConglomerateId(long)
+ *     rowLocationColumn(int)
+ *     ascend_column_info(FormatableBitSet)
+ *     collation_ids(compressed array of ints)
+ *
+ */
+
+/*
+ * @format_id ACCESS_B2I_V5_ID
+ *
+ * @purpose   The tag that describes the on disk representation of the B2I
+ *            conglomerate object.  Access contains no "directory" of 
+ *            conglomerate information.  In order to bootstrap opening a file
+ *            it encodes the factory that can open the conglomerate in the 
+ *            conglomerate id itself.  There exists a single B2IFactory which
+ *            must be able to read all btree format id's.  
+ *
  *            This format is the current version id of B2I and has been used 
- *            in versions of Derby after the 10.2 release.
+ *            in versions of Derby after the 10.3 release.
  *
  * @upgrade   This is the current version, no upgrade necessary.
  *
@@ -161,7 +225,7 @@
  *     rowLocationColumn(int)
  *     ascend_column_info(FormatableBitSet)
  *     collation_ids(compressed array of ints)
- *
+ *     isUniqueWithDuplicateNulls(boolean)
  */
 
 /**
@@ -187,7 +251,7 @@
     private static final String PROPERTY_BASECONGLOMID = "baseConglomerateId";
     private static final String PROPERTY_ROWLOCCOLUMN  = "rowLocationColumn";
 
-    static final int FORMAT_NUMBER = StoredFormatIds.ACCESS_B2I_V4_ID;
+    static final int FORMAT_NUMBER = StoredFormatIds.ACCESS_B2I_V5_ID;
 
 	/*
 	** Fields of B2I.
@@ -377,7 +441,7 @@
 
         return(cc);
     }
-
+    
     /**************************************************************************
 	 *  Private methods of B2I, arranged alphabetically.
      **************************************************************************
@@ -996,7 +1060,7 @@
 	*/
 	public int getTypeFormatId() 
     {
-		return StoredFormatIds.ACCESS_B2I_V4_ID;
+		return StoredFormatIds.ACCESS_B2I_V5_ID;
 	}
 
 
@@ -1037,18 +1101,34 @@
      *
      * @see java.io.Externalizable#writeExternal
      **/
-	public void writeExternal(ObjectOutput out) 
+	public void writeExternal_v10_3(ObjectOutput out) 
         throws IOException 
     {
         // First part of ACCESS_B2I_V4_ID format is the ACCESS_B2I_V3_ID format.
         writeExternal_v10_2(out);
-
-		if (conglom_format_id == StoredFormatIds.ACCESS_B2I_V4_ID)
+		if (conglom_format_id == StoredFormatIds.ACCESS_B2I_V4_ID
+                || conglom_format_id == StoredFormatIds.ACCESS_B2I_V5_ID)
         {
             // Now append sparse array of collation ids
             ConglomerateUtil.writeCollationIdArray(collation_ids, out);
         }
-	}
+    }
+
+
+    /**
+     * Store the stored representation of the column value in the
+     * stream.
+     * <p>
+     * For more detailed description of the ACCESS_B2I_V3_ID and 
+     * ACCESS_B2I_V5_ID formats see documentation at top of file.
+     *
+     * @see java.io.Externalizable#writeExternal
+     **/
+    public void writeExternal(ObjectOutput out) throws IOException {
+        writeExternal_v10_3 (out);
+        if (conglom_format_id == StoredFormatIds.ACCESS_B2I_V5_ID)
+            out.writeBoolean (isUniqueWithDuplicateNulls());
+    }
 
     /**
      * Restore the in-memory representation from the stream.
@@ -1064,7 +1144,6 @@
 		throws IOException, ClassNotFoundException
 	{
 		super.readExternal(in);
-		
 		baseConglomerateId = in.readLong();
 		rowLocationColumn  = in.readInt();
 
@@ -1074,7 +1153,7 @@
         ascDescInfo = new boolean[ascDescBits.getLength()];
         for(int i =0 ; i < ascDescBits.getLength(); i++)
             ascDescInfo[i] = ascDescBits.isSet(i);
-
+        
         // In memory maintain a collation id per column in the template.
         collation_ids = new int[format_ids.length];
 
@@ -1084,10 +1163,15 @@
         for (int i = 0; i < format_ids.length; i++)
             collation_ids[i] = StringDataValue.COLLATION_TYPE_UCS_BASIC;
 
-		if (conglom_format_id == StoredFormatIds.ACCESS_B2I_V4_ID)
+        // initialize the unique with null setting to false, to be reset
+        // below when read from disk.  For version ACCESS_B2I_V3_ID and
+        // ACCESS_B2I_V4_ID, this is the default and no resetting is necessary.
+        setUniqueWithDuplicateNulls(false);
+
+		if (conglom_format_id == StoredFormatIds.ACCESS_B2I_V4_ID
+                || conglom_format_id == StoredFormatIds.ACCESS_B2I_V5_ID)
         {
             // current format id, read collation info from disk
-
             if (SanityManager.DEBUG)
             {
                 // length must include row location column and at least
@@ -1101,7 +1185,7 @@
         }
         else if (conglom_format_id != StoredFormatIds.ACCESS_B2I_V3_ID)
         {
-            // Currently only V3 and V4 should be possible in a Derby DB.
+            // Currently only V3, V4 and V5 should be possible in a Derby DB.
             // Actual work for V3 is handled by default code above, so no
             // special work is necessary.
 
@@ -1110,6 +1194,9 @@
                 SanityManager.THROWASSERT(
                     "Unexpected format id: " + conglom_format_id);
             }
+        }
+        if (conglom_format_id == StoredFormatIds.ACCESS_B2I_V5_ID) {
+            setUniqueWithDuplicateNulls(in.readBoolean());
         }
 	}
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2IFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2IFactory.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2IFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2IFactory.java Tue Mar  4 09:38:48 2008
@@ -175,13 +175,25 @@
 
         if (xact_mgr.checkVersion(
                 RawStoreFactory.DERBY_STORE_MAJOR_VERSION_10,
-                RawStoreFactory.DERBY_STORE_MINOR_VERSION_3,
-                null))
+                RawStoreFactory.DERBY_STORE_MINOR_VERSION_4,
+                null)) 
         {
-            // on disk databases with version higher than 10.2 should use
+            // on disk databases with version higher than 10.3 should use
             // current disk format B2I.  This includes new databases or
             // hard upgraded databases.
             btree = new B2I();
+            
+        }
+        else if (xact_mgr.checkVersion(
+                RawStoreFactory.DERBY_STORE_MAJOR_VERSION_10,
+                RawStoreFactory.DERBY_STORE_MINOR_VERSION_3,
+                null))
+        {
+            // Old databases that are running in new versions of the software,
+            // but are running in soft upgrade mode at release level 10.3
+            // use the 10.3 B2I version.  This version will
+            // continue to write metadata that can be read by 10.3.
+            btree = new B2I_10_3();
         }
         else
         {

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I_10_3.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I_10_3.java?rev=633560&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I_10_3.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I_10_3.java Tue Mar  4 09:38:48 2008
@@ -0,0 +1,129 @@
+/*
+
+   Derby - Class org.apache.derby.impl.store.access.btree.index.B2I_10_3
+
+   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.store.access.btree.index;
+
+import java.io.IOException;
+import java.io.ObjectOutput;
+import org.apache.derby.iapi.services.io.StoredFormatIds;
+
+/*
+ * @format_id ACCESS_B2I_V4_ID
+ *
+ * @purpose   The tag that describes the on disk representation of the B2I
+ *            conglomerate object.  Access contains no "directory" of 
+ *            conglomerate information.  In order to bootstrap opening a file
+ *            it encodes the factory that can open the conglomerate in the 
+ *            conglomerate id itself.  There exists a single B2IFactory which
+ *            must be able to read all btree format id's.  
+ *
+ *            This format was used for all Derby database B2I's in version 10.3.
+ *
+ * @upgrade   The format id of this object is currently always read from disk
+ *            as the first field of the conglomerate itself.  A bootstrap
+ *            problem exists as we don't know the format id of the B2I 
+ *            until we are in the "middle" of reading the B2I.  Thus the
+ *            base B2I implementation must be able to read and write 
+ *            all formats based on the reading the 
+ *            "format_of_this_conglomerate". 
+ *
+ *            soft upgrade to ACCESS_B2I_V5_ID:
+ *                read:
+ *                    old format is readable by current B2I implementation,
+ *                    with automatic in memory creation of default
+ *                    isUniqueWithDuplicateNulls value of false.
+ *                    No code other than readExternal and writeExternal need 
+ *                    know about old format.
+ *                write:
+ *                    will never write out new format id in soft upgrade mode.
+ *                    Code in readExternal and writeExternal handles writing
+ *                    correct version.  Code in the factory handles making
+ *                    sure new conglomerates use the B2I_v10_3 class
+ *                    that will write out old format info.
+ *
+ *            hard upgrade to ACCESS_B2I_V5_ID:
+ *                read:
+ *                    old format is readable by current B2I implementation,
+ *                    with automatic in memory creation of default
+ *                    isUniqueWithDuplicateNulls value of false.
+ *
+ *                write:
+ *                    Only "lazy" upgrade will happen.  New format will only
+ *                    get written for new conglomerate created after the 
+ *                    upgrade.  Old conglomerates continue to be handled the
+ *                    same as soft upgrade.
+ *
+ * @disk_layout 
+ *     format_of_this_conlgomerate(byte[])
+ *     containerid(long)
+ *     segmentid(int)
+ *     number_of_key_fields(int)
+ *     number_of_unique_columns(int)
+ *     allow_duplicates(boolean)
+ *     maintain_parent_links(boolean)
+ *     array_of_format_ids(byte[][])
+ *     baseConglomerateId(long)
+ *     rowLocationColumn(int)
+ *     ascend_column_info(FormatableBitSet)
+ *     collation_ids(compressed array of ints)
+ *
+ */
+
+/**
+ * Class used to instantiate 10.3 version of the B2I object.
+ *
+ * This class implements the format of the B2I object as existed in 
+ * the 10.3 release of Derby.  In subsequent releases
+ * the format was enhanced to store the uniqueWithDuplicateNulls attribute
+ * of the index.
+ *
+ * For upgrade purpose all 10.3 and prior versions are assumed to have
+ * false for the uniqueWithDuplicateNulls attribute.
+ *
+ * This class reads and writes the V4 version to/from disk and reads/writes
+ * current in-memory version of the data structure.
+ *
+ */
+public class B2I_10_3 extends B2I {
+
+    /**
+     * Return my format identifier.
+     * @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
+     */
+    public int getTypeFormatId() {
+        return StoredFormatIds.ACCESS_B2I_V4_ID;
+    }
+
+    /**
+     * Store the stored representation of the column value in the
+     * stream.
+     * <p>
+     * For more detailed description of the format see documentation
+     * at top of file.
+     *
+     * @see java.io.Externalizable#writeExternal
+     **/
+    
+    public void writeExternal(ObjectOutput out) throws IOException {
+        writeExternal_v10_3(out);
+    }
+    
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2I_10_3.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/ExternalSortFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/ExternalSortFactory.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/ExternalSortFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/ExternalSortFactory.java Tue Mar  4 09:38:48 2008
@@ -119,6 +119,17 @@
 	{
 		return formatUUID;
 	}
+        
+	/**
+	 * Returns merge sort implementation. Extending classes can overide this 
+	 * method to customize sorting.
+     *
+	 * @returns MergeSort implementation
+	 */
+	protected MergeSort getMergeSort() 
+    {
+		return new MergeSort();
+	}
 
 	/*
 	** Methods of SortFactory
@@ -143,7 +154,7 @@
     int                     estimatedRowSize)
         throws StandardException
 	{
-		MergeSort sort = new MergeSort();
+		MergeSort sort = getMergeSort();
 
         // RESOLVE - mikem change this to use estimatedRows and 
         // estimatedRowSize to come up with a smarter number for sortBufferMax

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/MergeSort.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/MergeSort.java?rev=633560&r1=633559&r2=633560&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/MergeSort.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/MergeSort.java Tue Mar  4 09:38:48 2008
@@ -58,9 +58,8 @@
 
 **/
 
-final class MergeSort implements Sort
+class MergeSort implements Sort
 {
-
 	/*
 	 * Fields
 	 */
@@ -115,17 +114,17 @@
     column to compare.  To find the column id to compare as the i'th column
     look in columnOrderingMap[i].
 	**/
-	private int columnOrderingMap[];
+	protected int columnOrderingMap[];
 
 	/**
     A lookup table to speed up lookup of Ascending state of a column, 
 	**/
-	private boolean columnOrderingAscendingMap[];
+	protected boolean columnOrderingAscendingMap[];
 
 	/**
     A lookup table to speed up lookup of nulls-low ordering of a column, 
 	**/
-	private boolean columnOrderingNullsLowMap[];
+	protected boolean columnOrderingNullsLowMap[];
 
 	/**
 	The sort observer.  May be null.  Used as a callback.
@@ -478,7 +477,7 @@
         }
 	}
 
-	int compare(
+	protected int compare(
     DataValueDescriptor[] r1, 
     DataValueDescriptor[] r2)
 		throws StandardException

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/UniqueWithDuplicateNullsExternalSortFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/UniqueWithDuplicateNullsExternalSortFactory.java?rev=633560&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/UniqueWithDuplicateNullsExternalSortFactory.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/UniqueWithDuplicateNullsExternalSortFactory.java Tue Mar  4 09:38:48 2008
@@ -0,0 +1,47 @@
+/*
+
+   Derby - Class org.apache.derby.impl.store.access.sort.UniqueWithDuplicateNullsExternalSortFactory
+
+   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.store.access.sort;
+/**
+ * Method factory to support sorting of Almost unique index. This class 
+ * overrides getMergeSort of ExternalSortFactory to return UniqueWithDuplicateNullsMergeSort.
+ */
+public class UniqueWithDuplicateNullsExternalSortFactory extends ExternalSortFactory {
+    private static final String IMPLEMENTATIONID = "sort almost unique external";
+    
+    protected MergeSort getMergeSort() {
+        return new UniqueWithDuplicateNullsMergeSort ();
+    }
+
+    /**
+     * @see MethodFactory#primaryImplementationType
+     */
+    public String primaryImplementationType() {
+        return IMPLEMENTATIONID;
+    }
+
+    /**
+     * @see MethodFactory#supportsImplementation
+     */
+    public boolean supportsImplementation(String implementationId) {
+        return IMPLEMENTATIONID.equals (implementationId);
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/UniqueWithDuplicateNullsExternalSortFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message