You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by rh...@apache.org on 2022/08/21 18:20:01 UTC

svn commit: r1903614 [6/6] - in /db/derby/code/branches/10.15: ./ java/org.apache.derby.engine/org/apache/derby/impl/sql/compile/ java/org.apache.derby.engine/org/apache/derby/impl/sql/execute/ java/org.apache.derby.tests/org/apache/derbyTesting/functi...

Modified: db/derby/code/branches/10.15/java/org.apache.derby.engine/org/apache/derby/impl/sql/execute/TemporaryRowHolderImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.15/java/org.apache.derby.engine/org/apache/derby/impl/sql/execute/TemporaryRowHolderImpl.java?rev=1903614&r1=1903613&r2=1903614&view=diff
==============================================================================
--- db/derby/code/branches/10.15/java/org.apache.derby.engine/org/apache/derby/impl/sql/execute/TemporaryRowHolderImpl.java (original)
+++ db/derby/code/branches/10.15/java/org.apache.derby.engine/org/apache/derby/impl/sql/execute/TemporaryRowHolderImpl.java Sun Aug 21 18:20:01 2022
@@ -52,133 +52,133 @@ import java.util.Properties;
  */
 class TemporaryRowHolderImpl implements TemporaryRowHolder
 {
-	public static final int DEFAULT_OVERFLOWTHRESHOLD = 5;
+    public static final int DEFAULT_OVERFLOWTHRESHOLD = 5;
 
-	protected static final int STATE_UNINIT = 0;
-	protected static final int STATE_INSERT = 1;
-	protected static final int STATE_DRAIN = 2;
-
-
-	protected ExecRow[] 	rowArray;
-	protected int 		lastArraySlot;
-	private int			numRowsIn;
-	protected int		state = STATE_UNINIT;
-
-	private	long				    CID;
-	private boolean					conglomCreated;
-	private ConglomerateController	cc;
-	private Properties				properties;
-	private ScanController			scan;
-	private	ResultDescription		resultDescription;
-	/** Activation object with local state information. */
-	Activation						activation;
-
-	private boolean     isUniqueStream;
-
-	/* beetle 3865 updateable cursor use index. A virtual memory heap is a heap that has in-memory
-	 * part to get better performance, less overhead. No position index needed. We read from and write
-	 * to the in-memory part as much as possible. And we can insert after we start retrieving results.
-	 * Could be used for other things too.
-	 */
-	private boolean     isVirtualMemHeap;
-	private boolean     uniqueIndexCreated;
-	private boolean     positionIndexCreated;
-	private long        uniqueIndexConglomId;
-	private long        positionIndexConglomId;
-	private ConglomerateController uniqueIndex_cc;
-	private ConglomerateController positionIndex_cc;
-	private DataValueDescriptor[]  uniqueIndexRow = null;
-	private DataValueDescriptor[]  positionIndexRow = null;
-	private RowLocation            destRowLocation; //row location in the temporary conglomerate
-	private SQLLongint             position_sqllong;
+    protected static final int STATE_UNINIT = 0;
+    protected static final int STATE_INSERT = 1;
+    protected static final int STATE_DRAIN = 2;
+
+
+    protected ExecRow[] 	rowArray;
+    protected int 		lastArraySlot;
+    private int			numRowsIn;
+    protected int		state = STATE_UNINIT;
+
+    private	long				    CID;
+    private boolean					conglomCreated;
+    private ConglomerateController	cc;
+    private Properties				properties;
+    private ScanController			scan;
+    private	ResultDescription		resultDescription;
+    /** Activation object with local state information. */
+    Activation						activation;
+
+    private boolean     isUniqueStream;
+
+    /* beetle 3865 updateable cursor use index. A virtual memory heap is a heap that has in-memory
+     * part to get better performance, less overhead. No position index needed. We read from and write
+     * to the in-memory part as much as possible. And we can insert after we start retrieving results.
+     * Could be used for other things too.
+     */
+    private boolean     isVirtualMemHeap;
+    private boolean     uniqueIndexCreated;
+    private boolean     positionIndexCreated;
+    private long        uniqueIndexConglomId;
+    private long        positionIndexConglomId;
+    private ConglomerateController uniqueIndex_cc;
+    private ConglomerateController positionIndex_cc;
+    private DataValueDescriptor[]  uniqueIndexRow = null;
+    private DataValueDescriptor[]  positionIndexRow = null;
+    private RowLocation            destRowLocation; //row location in the temporary conglomerate
+    private SQLLongint             position_sqllong;
 	
 
-	/**
-	 * Uses the default overflow to
- 	 * a conglomerate threshold (5).
-	 *
-	 * @param activation the activation
-	 * @param properties the properties of the original table.  Used
-	 *		to help the store use optimal page size, etc.
-	 * @param resultDescription the result description.  Relevant for the getResultDescription
-	 * 		call on the result set returned by getResultSet.  May be null
-	 */
-	public TemporaryRowHolderImpl
-	(
-		Activation				activation, 
-		Properties 				properties, 
-		ResultDescription		resultDescription
-	) 
-	{
-		this(activation, properties, resultDescription,
-			 DEFAULT_OVERFLOWTHRESHOLD, false, false);
-	}
+    /**
+     * Uses the default overflow to
+     * a conglomerate threshold (5).
+     *
+     * @param activation the activation
+     * @param properties the properties of the original table.  Used
+     *		to help the store use optimal page size, etc.
+     * @param resultDescription the result description.  Relevant for the getResultDescription
+     * 		call on the result set returned by getResultSet.  May be null
+     */
+    public TemporaryRowHolderImpl
+    (
+        Activation				activation, 
+        Properties 				properties, 
+        ResultDescription		resultDescription
+    ) 
+    {
+        this(activation, properties, resultDescription,
+             DEFAULT_OVERFLOWTHRESHOLD, false, false);
+    }
 	
-	/**
-	 * Uses the default overflow to
- 	 * a conglomerate threshold (5).
-	 *
-	 * @param activation the activation
-	 * @param properties the properties of the original table.  Used
-	 *		to help the store use optimal page size, etc.
-	 * @param resultDescription the result description.  Relevant for the getResultDescription
-	 * 		call on the result set returned by getResultSet.  May be null
-	 * @param isUniqueStream - true , if it has to be temporary row holder unique stream
-	 */
-	public TemporaryRowHolderImpl
-	(
-		Activation				activation, 
-		Properties 				properties, 
-		ResultDescription		resultDescription,
-		boolean                 isUniqueStream
-	) 
-	{
-		this(activation, properties, resultDescription, 1, isUniqueStream,
-			 false);
-	}
-
-
-	/**
-	 * Create a temporary row holder with the defined overflow to conglom
-	 *
-	 * @param activation the activation
-	 * @param properties the properties of the original table.  Used
-	 *		to help the store use optimal page size, etc.
-	 * @param resultDescription the result description.  Relevant for the getResultDescription
-	 * 		call on the result set returned by getResultSet.  May be null
-	 * @param overflowToConglomThreshold on an attempt to insert
-	 * 		this number of rows, the rows will be put
- 	 *		into a temporary conglomerate.
-	 */
-	public TemporaryRowHolderImpl
-	(
-		Activation			 	activation, 
-		Properties				properties,
-		ResultDescription		resultDescription,
-		int 					overflowToConglomThreshold,
-		boolean                 isUniqueStream,
-		boolean					isVirtualMemHeap
-	)
-	{
-		if (SanityManager.DEBUG)
-		{
-			if (overflowToConglomThreshold <= 0)
-			{
-				SanityManager.THROWASSERT("It is assumed that "+
-					"the overflow threshold is > 0.  "+
-					"If you you need to change this you have to recode some of "+
-					"this class.");
-			}
-		}
-
-		this.activation = activation;
-		this.properties = properties;
-		this.resultDescription = resultDescription;
-		this.isUniqueStream = isUniqueStream;
-		this.isVirtualMemHeap = isVirtualMemHeap;
-		rowArray = new ExecRow[overflowToConglomThreshold];
-		lastArraySlot = -1;
-	}
+    /**
+     * Uses the default overflow to
+     * a conglomerate threshold (5).
+     *
+     * @param activation the activation
+     * @param properties the properties of the original table.  Used
+     *		to help the store use optimal page size, etc.
+     * @param resultDescription the result description.  Relevant for the getResultDescription
+     * 		call on the result set returned by getResultSet.  May be null
+     * @param isUniqueStream - true , if it has to be temporary row holder unique stream
+     */
+    public TemporaryRowHolderImpl
+    (
+        Activation				activation, 
+        Properties 				properties, 
+        ResultDescription		resultDescription,
+        boolean                 isUniqueStream
+    ) 
+    {
+        this(activation, properties, resultDescription, 1, isUniqueStream,
+             false);
+    }
+
+
+    /**
+     * Create a temporary row holder with the defined overflow to conglom
+     *
+     * @param activation the activation
+     * @param properties the properties of the original table.  Used
+     *		to help the store use optimal page size, etc.
+     * @param resultDescription the result description.  Relevant for the getResultDescription
+     * 		call on the result set returned by getResultSet.  May be null
+     * @param overflowToConglomThreshold on an attempt to insert
+     * 		this number of rows, the rows will be put
+     *		into a temporary conglomerate.
+     */
+    public TemporaryRowHolderImpl
+    (
+        Activation			 	activation, 
+        Properties				properties,
+        ResultDescription		resultDescription,
+        int 					overflowToConglomThreshold,
+        boolean                 isUniqueStream,
+        boolean					isVirtualMemHeap
+    )
+    {
+        if (SanityManager.DEBUG)
+        {
+            if (overflowToConglomThreshold <= 0)
+            {
+                SanityManager.THROWASSERT("It is assumed that "+
+                                          "the overflow threshold is > 0.  "+
+                                          "If you you need to change this you have to recode some of "+
+                                          "this class.");
+            }
+        }
+
+        this.activation = activation;
+        this.properties = properties;
+        this.resultDescription = resultDescription;
+        this.isUniqueStream = isUniqueStream;
+        this.isVirtualMemHeap = isVirtualMemHeap;
+        rowArray = new ExecRow[overflowToConglomThreshold];
+        lastArraySlot = -1;
+    }
 
     /* Avoid materializing a stream just because it goes through a temp table.
      * It is OK to have a stream in the temp table (in memory or spilled to
@@ -199,67 +199,67 @@ class TemporaryRowHolderImpl implements
      *
      * Beetle 4896.
      */
-	private ExecRow cloneRow(ExecRow inputRow)
-	{
-		DataValueDescriptor[] cols = inputRow.getRowArray();
-		int ncols = cols.length;
-		ExecRow cloned = ((ValueRow) inputRow).cloneMe();
-		for (int i = 0; i < ncols; i++)
-		{
-			if (cols[i] != null)
-			{
-				/* Rows are 1-based, cols[] is 0-based */
+    private ExecRow cloneRow(ExecRow inputRow)
+    {
+        DataValueDescriptor[] cols = inputRow.getRowArray();
+        int ncols = cols.length;
+        ExecRow cloned = ((ValueRow) inputRow).cloneMe();
+        for (int i = 0; i < ncols; i++)
+        {
+            if (cols[i] != null)
+            {
+                /* Rows are 1-based, cols[] is 0-based */
                 cloned.setColumn(i + 1, cols[i].cloneHolder());
-			}
-		}
-		if (inputRow instanceof IndexValueRow)
-			return new IndexValueRow(cloned);
-		else
-			return cloned;
-	}
-
-	/**
-	 * Insert a row
-	 *
-	 * @param inputRow the row to insert 
-	 *
-	 * @exception StandardException on error
- 	 */
-	public void insert(ExecRow inputRow)
-		throws StandardException
-	{
-
-		if (SanityManager.DEBUG)
-		{
-			if(!isUniqueStream && !isVirtualMemHeap)
-				SanityManager.ASSERT(state != STATE_DRAIN, "you cannot insert rows after starting to drain");
-		}
-		if (! isVirtualMemHeap)
-			state = STATE_INSERT;
-
-		if(uniqueIndexCreated)
-		{
-			if(isRowAlreadyExist(inputRow))
-				return;
-		}
-
-		numRowsIn++;
-
-		if (lastArraySlot + 1 < rowArray.length)
-		{
-			rowArray[++lastArraySlot] = cloneRow(inputRow);
+            }
+        }
+        if (inputRow instanceof IndexValueRow)
+            return new IndexValueRow(cloned);
+        else
+            return cloned;
+    }
+
+    /**
+     * Insert a row
+     *
+     * @param inputRow the row to insert 
+     *
+     * @exception StandardException on error
+     */
+    public void insert(ExecRow inputRow)
+        throws StandardException
+    {
+
+        if (SanityManager.DEBUG)
+        {
+            if(!isUniqueStream && !isVirtualMemHeap)
+                SanityManager.ASSERT(state != STATE_DRAIN, "you cannot insert rows after starting to drain");
+        }
+        if (! isVirtualMemHeap)
+            state = STATE_INSERT;
+
+        if(uniqueIndexCreated)
+        {
+            if(isRowAlreadyExist(inputRow))
+                return;
+        }
+
+        numRowsIn++;
+
+        if (lastArraySlot + 1 < rowArray.length)
+        {
+            rowArray[++lastArraySlot] = cloneRow(inputRow);
 			
-			//In case of unique stream we push every thing into the
-			// conglomerates for time being, we keep one row in the array for
-			// the template.
+            //In case of unique stream we push every thing into the
+            // conglomerates for time being, we keep one row in the array for
+            // the template.
             if (!isUniqueStream) {
-				return;  
+                return;  
             }
-		}
+        }
 			
-		if (!conglomCreated)
-		{
-			TransactionController tc = activation.getTransactionController();
+        if (!conglomCreated)
+        {
+            TransactionController tc = activation.getTransactionController();
 
             // TODO-COLLATE, I think collation needs to get set always correctly
             // but did see what to get collate id when there was no result
@@ -278,107 +278,147 @@ class TemporaryRowHolderImpl implements
             int collation_ids[] = null;
 
             /*
-            TODO-COLLATE - if we could count on resultDescription I think the
-            following would work.
-
-            if (resultDescription != null)
-            {
-                // init collation id info from resultDescription for create call
-                collation_ids = new int[resultDescription.getColumnCount()];
+              TODO-COLLATE - if we could count on resultDescription I think the
+              following would work.
 
-                for (int i = 0; i < collation_ids.length; i++)
-                {
-                    collation_ids[i] = 
-                        resultDescription.getColumnDescriptor(
-                            i + 1).getType().getCollationType();
-                }
-            }
+              if (resultDescription != null)
+              {
+              // init collation id info from resultDescription for create call
+              collation_ids = new int[resultDescription.getColumnCount()];
+
+              for (int i = 0; i < collation_ids.length; i++)
+              {
+              collation_ids[i] = 
+              resultDescription.getColumnDescriptor(
+              i + 1).getType().getCollationType();
+              }
+              }
             */
 
 
-			/*
-			** Create the conglomerate with the template row.
-			*/
-			CID = 
+            /*
+            ** Create the conglomerate with the template row.
+            */
+            CID = 
                 tc.createConglomerate(
                     "heap",
-                    inputRow.getRowArray(),
+                    makeTemplateRow(inputRow.getRowArray()),
                     null, //column sort order - not required for heap
                     collation_ids,
                     properties,
                     TransactionController.IS_TEMPORARY | 
                     TransactionController.IS_KEPT);
 
-			conglomCreated = true;
+            conglomCreated = true;
+
+            cc = tc.openConglomerate(CID, 
+                                     false,
+                                     TransactionController.OPENMODE_FORUPDATE,
+                                     TransactionController.MODE_TABLE,
+                                     TransactionController.ISOLATION_SERIALIZABLE);
+            if(isUniqueStream)
+                destRowLocation = cc.newRowLocationTemplate();
 
-			cc = tc.openConglomerate(CID, 
-                                false,
-                                TransactionController.OPENMODE_FORUPDATE,
-                                TransactionController.MODE_TABLE,
-                                TransactionController.ISOLATION_SERIALIZABLE);
-			if(isUniqueStream)
-			   destRowLocation = cc.newRowLocationTemplate();
-
-		}
-
-		int status = 0;
-		if(isUniqueStream)
-		{
-			cc.insertAndFetchLocation(inputRow.getRowArray(), destRowLocation);
-			insertToPositionIndex(numRowsIn -1, destRowLocation);
-			//create the unique index based on input row ROW Location
-			if(!uniqueIndexCreated)
-				isRowAlreadyExist(inputRow);
+        }
+
+        int status = 0;
+        if(isUniqueStream)
+        {
+            cc.insertAndFetchLocation(inputRow.getRowArray(), destRowLocation);
+            insertToPositionIndex(numRowsIn -1, destRowLocation);
+            //create the unique index based on input row ROW Location
+            if(!uniqueIndexCreated)
+                isRowAlreadyExist(inputRow);
 
-		}else
-		{
+        }else
+        {
             status = cc.insert(inputRow.getRowArray());
-			if (isVirtualMemHeap)
-				state = STATE_INSERT;
-		}
-
-		if (SanityManager.DEBUG)
-		{
-			if (status != 0)
-			{
-				SanityManager.THROWASSERT("got funky status ("+status+") back from "+
-						"ConglomerateConstroller.insert()");
-			}
-		}
-	}
-
-
-	/**
-	 * Maintain an unique index based on the input row's row location in the
-	 * base table, this index make sures that we don't insert duplicate rows 
-	 * into the temporary heap.
-	 * @param inputRow  the row we are inserting to temporary row holder 
-	 * @exception StandardException on error
- 	 */
-
-
-	private boolean isRowAlreadyExist(ExecRow inputRow) throws  StandardException
-	{
-		DataValueDescriptor		rlColumn;
-		RowLocation	baseRowLocation;
-		rlColumn = inputRow.getColumn(inputRow.nColumns());
-
-		if(CID!=0 && rlColumn instanceof SQLRef)
-		{
-			baseRowLocation = 
-				(RowLocation) (rlColumn).getObject();
+            if (isVirtualMemHeap)
+                state = STATE_INSERT;
+        }
+
+        if (SanityManager.DEBUG)
+        {
+            if (status != 0)
+            {
+                SanityManager.THROWASSERT("got funky status ("+status+") back from "+
+                                          "ConglomerateConstroller.insert()");
+            }
+        }
+    }
+
+    /**
+     * Make a template row for creating the spillover conglomerate. We only do this
+     * for MERGE statments. MERGE processing can temporarily stuff literal nulls into
+     * the DataValueDescriptor slot for an autoincrement column. The null DataValueDescriptor
+     * can not be used to create the layout of the spillover conglomerate. See DERBY-7144.
+     *
+     * @param originalRow The original row
+     */
+    private DataValueDescriptor[] makeTemplateRow(DataValueDescriptor[] originalRow)
+        throws StandardException {
+
+        boolean useOriginalRow = true;
+
+        for (DataValueDescriptor dvd : originalRow)
+        {
+            if (dvd == null)
+            {
+                useOriginalRow = false;
+                break;
+            }
+        }
+
+        if (useOriginalRow) { return originalRow; }
+        else
+        {
+            int columnCount = resultDescription.getColumnCount();
+            ExecRow emptyRow = activation.getExecutionFactory().getValueRow(columnCount);
+            
+            for (int idx = 1; idx <= columnCount; idx++)
+            {
+                emptyRow.setColumn
+                    (
+                        idx,
+                        resultDescription.getColumnDescriptor(idx).getType().getNullabilityType(true).getNull()
+                    );
+            }
+
+            return emptyRow.getRowArray();
+        }
+    }
+
+    /**
+     * Maintain an unique index based on the input row's row location in the
+     * base table, this index make sures that we don't insert duplicate rows 
+     * into the temporary heap.
+     * @param inputRow  the row we are inserting to temporary row holder 
+     * @exception StandardException on error
+     */
+
+
+    private boolean isRowAlreadyExist(ExecRow inputRow) throws  StandardException
+    {
+        DataValueDescriptor		rlColumn;
+        RowLocation	baseRowLocation;
+        rlColumn = inputRow.getColumn(inputRow.nColumns());
+
+        if(CID!=0 && rlColumn instanceof SQLRef)
+        {
+            baseRowLocation = 
+                (RowLocation) (rlColumn).getObject();
 		
-			if(!uniqueIndexCreated)
-			{
-				TransactionController tc =
-					activation.getTransactionController();
-				int numKeys = 2;
-				uniqueIndexRow = new DataValueDescriptor[numKeys];
-				uniqueIndexRow[0] = baseRowLocation;
-				uniqueIndexRow[1] = baseRowLocation;
-				Properties props = makeIndexProperties(uniqueIndexRow, CID);
-				uniqueIndexConglomId =
-					tc.createConglomerate(
+            if(!uniqueIndexCreated)
+            {
+                TransactionController tc =
+                    activation.getTransactionController();
+                int numKeys = 2;
+                uniqueIndexRow = new DataValueDescriptor[numKeys];
+                uniqueIndexRow[0] = baseRowLocation;
+                uniqueIndexRow[1] = baseRowLocation;
+                Properties props = makeIndexProperties(uniqueIndexRow, CID);
+                uniqueIndexConglomId =
+                    tc.createConglomerate(
                         "BTREE",
                         uniqueIndexRow, 
                         null,  
@@ -387,63 +427,63 @@ class TemporaryRowHolderImpl implements
                         (TransactionController.IS_TEMPORARY | 
                          TransactionController.IS_KEPT));
 
-				uniqueIndex_cc = tc.openConglomerate(
-								uniqueIndexConglomId, 
-								false,
-								TransactionController.OPENMODE_FORUPDATE,
-								TransactionController.MODE_TABLE,
-								TransactionController.ISOLATION_SERIALIZABLE);
-				uniqueIndexCreated = true;
-			}
-
-			uniqueIndexRow[0] = baseRowLocation;
-			uniqueIndexRow[1] = baseRowLocation;
-			// Insert the row into the secondary index.
-			int status;
+                uniqueIndex_cc = tc.openConglomerate(
+                    uniqueIndexConglomId, 
+                    false,
+                    TransactionController.OPENMODE_FORUPDATE,
+                    TransactionController.MODE_TABLE,
+                    TransactionController.ISOLATION_SERIALIZABLE);
+                uniqueIndexCreated = true;
+            }
+
+            uniqueIndexRow[0] = baseRowLocation;
+            uniqueIndexRow[1] = baseRowLocation;
+            // Insert the row into the secondary index.
+            int status;
             if ((status = uniqueIndex_cc.insert(uniqueIndexRow))!= 0)
-			{
-				if(status == ConglomerateController.ROWISDUPLICATE)
-				{
-					return true ; // okay; we don't insert duplicates
-				}
-				else
-				{
-					if (SanityManager.DEBUG)
-					{
-						if (status != 0)
-						{
-							SanityManager.THROWASSERT("got funky status ("+status+") back from "+
-													  "Unique Index insert()");
-						}
-					}
-				}
-			}
-		}
-
-		return false;
-	}
-
-
-	/**
-	 * Maintain an index that will allow us to read  from the 
-	 * temporary heap in the order we inserted.
-	 * @param position - the number of the row we are inserting into heap
-	 * @param rl the row to Location in the temporary heap 
-	 * @exception StandardException on error
- 	 */
-
-	private void insertToPositionIndex(int position, RowLocation rl ) throws  StandardException
-	{
-		if(!positionIndexCreated)
-		{
-			TransactionController tc = activation.getTransactionController();
-			int numKeys = 2;
-			position_sqllong = new SQLLongint();
-			positionIndexRow = new DataValueDescriptor[numKeys];
-			positionIndexRow[0] = position_sqllong;
-			positionIndexRow[1] = rl;				
-			Properties props = makeIndexProperties(positionIndexRow, CID);
-			positionIndexConglomId =
+            {
+                if(status == ConglomerateController.ROWISDUPLICATE)
+                {
+                    return true ; // okay; we don't insert duplicates
+                }
+                else
+                {
+                    if (SanityManager.DEBUG)
+                    {
+                        if (status != 0)
+                        {
+                            SanityManager.THROWASSERT("got funky status ("+status+") back from "+
+                                                      "Unique Index insert()");
+                        }
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Maintain an index that will allow us to read  from the 
+     * temporary heap in the order we inserted.
+     * @param position - the number of the row we are inserting into heap
+     * @param rl the row to Location in the temporary heap 
+     * @exception StandardException on error
+     */
+
+    private void insertToPositionIndex(int position, RowLocation rl ) throws  StandardException
+    {
+        if(!positionIndexCreated)
+        {
+            TransactionController tc = activation.getTransactionController();
+            int numKeys = 2;
+            position_sqllong = new SQLLongint();
+            positionIndexRow = new DataValueDescriptor[numKeys];
+            positionIndexRow[0] = position_sqllong;
+            positionIndexRow[1] = rl;				
+            Properties props = makeIndexProperties(positionIndexRow, CID);
+            positionIndexConglomId =
                 tc.createConglomerate(
                     "BTREE",
                     positionIndexRow, 
@@ -453,7 +493,7 @@ class TemporaryRowHolderImpl implements
                     (TransactionController.IS_TEMPORARY | 
                      TransactionController.IS_KEPT));
 
-			positionIndex_cc = 
+            positionIndex_cc = 
                 tc.openConglomerate(
                     positionIndexConglomId, 
                     false,
@@ -461,63 +501,63 @@ class TemporaryRowHolderImpl implements
                     TransactionController.MODE_TABLE,
                     TransactionController.ISOLATION_SERIALIZABLE);
 
-			positionIndexCreated = true;
-		}
+            positionIndexCreated = true;
+        }
 		
-		position_sqllong.setValue(position);
-		positionIndexRow[0] = position_sqllong;
-		positionIndexRow[1] = rl;
-		//insert the row location to position index
+        position_sqllong.setValue(position);
+        positionIndexRow[0] = position_sqllong;
+        positionIndexRow[1] = rl;
+        //insert the row location to position index
         positionIndex_cc.insert(positionIndexRow);
-	}
+    }
+
+    /**
+     * Get a result set for scanning what has been inserted
+     * so far.
+     *
+     * @return a result set to use
+     */
+    public CursorResultSet getResultSet()
+    {
+        state = STATE_DRAIN;
+        TransactionController tc = activation.getTransactionController();
+        if(isUniqueStream)
+        {
+            return new TemporaryRowHolderResultSet(tc, rowArray,
+                                                   resultDescription, isVirtualMemHeap,
+                                                   true, positionIndexConglomId, this);
+        }
+        else
+        {
+            return new TemporaryRowHolderResultSet(tc, rowArray, resultDescription, isVirtualMemHeap, this);
+
+        }
+    }
 
-	/**
-	 * Get a result set for scanning what has been inserted
- 	 * so far.
-	 *
-	 * @return a result set to use
-	 */
-	public CursorResultSet getResultSet()
-	{
-		state = STATE_DRAIN;
-		TransactionController tc = activation.getTransactionController();
-		if(isUniqueStream)
-		{
-			return new TemporaryRowHolderResultSet(tc, rowArray,
-												   resultDescription, isVirtualMemHeap,
-												   true, positionIndexConglomId, this);
-		}
-		else
-		{
-			return new TemporaryRowHolderResultSet(tc, rowArray, resultDescription, isVirtualMemHeap, this);
-
-		}
-	}
-
-	/**
-	 * Purge the row holder of all its rows.
-	 * Resets the row holder so that it can
-	 * accept new inserts.  A cheap way to
-	 * recycle a row holder.
-	 *
-	 * @exception StandardException on error
-	 */
-	public void truncate() throws StandardException
-	{
-		close();
+    /**
+     * Purge the row holder of all its rows.
+     * Resets the row holder so that it can
+     * accept new inserts.  A cheap way to
+     * recycle a row holder.
+     *
+     * @exception StandardException on error
+     */
+    public void truncate() throws StandardException
+    {
+        close();
         if (SanityManager.DEBUG) {
             SanityManager.ASSERT(lastArraySlot == -1);
             SanityManager.ASSERT(state == STATE_UNINIT);
             SanityManager.ASSERT(!conglomCreated);
             SanityManager.ASSERT(CID == 0);
         }
-		for (int i = 0; i < rowArray.length; i++)
-		{
-			rowArray[i] = null;
-		}
+        for (int i = 0; i < rowArray.length; i++)
+        {
+            rowArray[i] = null;
+        }
 
-		numRowsIn = 0;
-	}
+        numRowsIn = 0;
+    }
 
     /**
      * Accessor to get the id of the temporary conglomerate. Temporary 
@@ -525,99 +565,99 @@ class TemporaryRowHolderImpl implements
      * temporary conglomerate has been created.
      * @return Conglomerate ID of temporary conglomerate
      */
-	public long getTemporaryConglomId()
-	{
+    public long getTemporaryConglomId()
+    {
         if (SanityManager.DEBUG) {
             SanityManager.ASSERT(CID == 0 && !conglomCreated || 
-                    CID < 0 && conglomCreated);
+                                 CID < 0 && conglomCreated);
+        }
+        return CID;
+    }
+
+    public long getPositionIndexConglomId()
+    {
+        return positionIndexConglomId;
+    }
+
+
+
+    private Properties makeIndexProperties(DataValueDescriptor[]
+                                           indexRowArray, long conglomId ) throws StandardException {
+        int nCols = indexRowArray.length;
+        Properties props = new Properties();
+        props.put("allowDuplicates", "false");
+        // all columns form the key, (currently) required
+        props.put("nKeyFields", String.valueOf(nCols));
+        props.put("nUniqueColumns", String.valueOf(nCols-1));
+        props.put("rowLocationColumn", String.valueOf(nCols-1));
+        props.put("baseConglomerateId", String.valueOf(conglomId));
+        return props;
+    }
+
+    public void setRowHolderTypeToUniqueStream()
+    {
+        isUniqueStream = true;
+    }
+
+    /**
+     * Clean up
+     *
+     * @exception StandardException on error
+     */
+    public void close() throws StandardException
+    {
+        if (scan != null)
+        {
+            scan.close();
+            scan = null;
         }
-		return CID;
-	}
 
-	public long getPositionIndexConglomId()
-	{
-		return positionIndexConglomId;
-	}
-
-
-
-	private Properties makeIndexProperties(DataValueDescriptor[]
-											   indexRowArray, long conglomId ) throws StandardException {
-		int nCols = indexRowArray.length;
-		Properties props = new Properties();
-		props.put("allowDuplicates", "false");
-		// all columns form the key, (currently) required
-		props.put("nKeyFields", String.valueOf(nCols));
-		props.put("nUniqueColumns", String.valueOf(nCols-1));
-		props.put("rowLocationColumn", String.valueOf(nCols-1));
-		props.put("baseConglomerateId", String.valueOf(conglomId));
-		return props;
-	}
-
-	public void setRowHolderTypeToUniqueStream()
-	{
-		isUniqueStream = true;
-	}
-
-	/**
-	 * Clean up
-	 *
-	 * @exception StandardException on error
-	 */
-	public void close() throws StandardException
-	{
-		if (scan != null)
-		{
-			scan.close();
-			scan = null;
-		}
-
-		if (cc != null)
-		{
-			cc.close();
-			cc = null;
-		}
-
-		if (uniqueIndex_cc != null)
-		{
-			uniqueIndex_cc.close();
-			uniqueIndex_cc = null;
-		}
-
-		if (positionIndex_cc != null)
-		{
-			positionIndex_cc.close();
-			positionIndex_cc = null;
-		}
-
-		TransactionController tc = activation.getTransactionController();
-
-		if (uniqueIndexCreated)
-		{
-			tc.dropConglomerate(uniqueIndexConglomId);
-			uniqueIndexCreated = false;
-		}
-
-		if (positionIndexCreated)
-		{
-			tc.dropConglomerate(positionIndexConglomId);
-			positionIndexCreated = false;
-		}
-
-		if (conglomCreated)
-		{
-			tc.dropConglomerate(CID);
-			conglomCreated = false;
+        if (cc != null)
+        {
+            cc.close();
+            cc = null;
+        }
+
+        if (uniqueIndex_cc != null)
+        {
+            uniqueIndex_cc.close();
+            uniqueIndex_cc = null;
+        }
+
+        if (positionIndex_cc != null)
+        {
+            positionIndex_cc.close();
+            positionIndex_cc = null;
+        }
+
+        TransactionController tc = activation.getTransactionController();
+
+        if (uniqueIndexCreated)
+        {
+            tc.dropConglomerate(uniqueIndexConglomId);
+            uniqueIndexCreated = false;
+        }
+
+        if (positionIndexCreated)
+        {
+            tc.dropConglomerate(positionIndexConglomId);
+            positionIndexCreated = false;
+        }
+
+        if (conglomCreated)
+        {
+            tc.dropConglomerate(CID);
+            conglomCreated = false;
             CID = 0;
-		} 
+        } 
         else 
         {
             if (SanityManager.DEBUG) {
                 SanityManager.ASSERT(CID == 0, "CID(" + CID + ")==0");
             }
         }
-		state = STATE_UNINIT;
-		lastArraySlot = -1;
-	}
+        state = STATE_UNINIT;
+        lastArraySlot = -1;
+    }
 }
 

Modified: db/derby/code/branches/10.15/java/org.apache.derby.tests/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.15/java/org.apache.derby.tests/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java?rev=1903614&r1=1903613&r2=1903614&view=diff
==============================================================================
--- db/derby/code/branches/10.15/java/org.apache.derby.tests/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java (original)
+++ db/derby/code/branches/10.15/java/org.apache.derby.tests/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java Sun Aug 21 18:20:01 2022
@@ -9561,6 +9561,128 @@ public class MergeStatementTest extends
 
     }
     
+    public void test_063_Derby7144() throws SQLException {
+        Connection  conn = openUserConnection( TEST_DBO );
+        
+        goodStatement(conn, "CREATE TABLE targetData_7144 (ID BIGINT GENERATED ALWAYS AS IDENTITY)");
+        goodStatement(conn, "CREATE TABLE sourceData_7144(CATEGORY INTEGER)");
+        goodStatement(conn, "INSERT INTO sourceData_7144 VALUES (10), (20)");
+
+        // the following statement used to kill the connection and raise the following error:
+        //
+        // ERROR XJ001: Java exception: 'ASSERT FAILED row template is null for column[0].: org.apache.derby.shared.common.sanity.AsserFailure'.
+        goodStatement
+            (
+                conn,
+                "MERGE INTO targetData_7144 target USING sourceData_7144 source ON 1 < 2\n" +
+                "WHEN NOT MATCHED THEN INSERT (id) VALUES (DEFAULT)\n"
+            );
+        assertResults
+            (
+                conn,
+                "SELECT * FROM targetData_7144",
+                new String[][]
+                {
+                    { "1" },   
+                    { "2" },    
+                },
+                false
+            );
+
+        // next test
+        goodStatement(conn, "CREATE TABLE targetData3_7144 (ID BIGINT GENERATED BY DEFAULT AS IDENTITY)");
+        goodStatement
+            (
+                conn,
+                "MERGE INTO targetData3_7144 target USING sourceData_7144 source ON 1 < 2\n" +
+                "WHEN NOT MATCHED THEN INSERT (id) VALUES (100)\n"
+            );
+        assertResults
+            (
+                conn,
+                "SELECT * FROM targetData3_7144",
+                new String[][]
+                {
+                    { "100" },   
+                    { "100" },    
+                },
+                false
+            );
+
+        // next test
+        goodStatement
+            (
+                conn,
+                "CREATE TABLE targetData_2_7144 (\n" +
+                "	  ID        BIGINT    PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,\n" +
+                "	  CATEGORY  INTEGER  NOT NULL,\n" +
+                "	  VALUE     DOUBLE    NOT NULL,\n" +
+                "	  ATTIME    TIMESTAMP NOT NULL,\n" +
+                "	  AGGDATE   DATE      NOT NULL,\n" +
+                "	  AGGCOUNT  INTEGER   NOT NULL,\n" +
+                "\n" +
+                "	  UNIQUE    (AGGDATE, CATEGORY)\n" +
+                "	)\n"
+            );
+         goodStatement
+            (
+                conn,
+                "CREATE TABLE sourceData2_7144\n" +
+                "(\n" +
+                "	  CATEGORY  INTEGER,\n" +
+                "	  VALUE     DOUBLE,\n" +
+                "	  ATTIME    TIMESTAMP,\n" +
+                "	  AGGDATE   DATE,\n" +
+                "	  AGGCOUNT  INTEGER\n" +
+                ")\n"
+            );
+         goodStatement
+            (
+                conn,
+                "INSERT INTO sourceData2_7144 VALUES\n" +
+                "(1, 45.67, TIMESTAMP('2022-07-29 01:24:21.336'), DATE('2022-07-29'), 3),\n" +
+                "(4, 37.15, TIMESTAMP('2022-07-29 01:24:21.336'), DATE('2022-07-29'), 3),\n" +
+                "(2, 123.34, TIMESTAMP('2022-07-31 01:38:24.66'), DATE('2022-07-31'), 3),\n" +
+                "(1, 78.9, TIMESTAMP('2022-07-31 01:38:24.66'), DATE('2022-07-31'), 2),\n" +
+                "(3, 1.2, TIMESTAMP('2022-07-31 01:38:24.66'), DATE('2022-07-31'), 1),\n" +
+                "(4, 5.6, TIMESTAMP('2022-07-31 01:38:24.66'), DATE('2022-07-31'), 1)\n"
+            );
+
+         // this statement used to kill the connection and raise the following error:
+         //
+         // ERROR XJ001: Java exception: 'ASSERT FAILED row template is null for column[0].: org.apache.derby.shared.common.sanity.AssetFailure'.
+         goodStatement
+            (
+                conn,
+                "MERGE INTO targetData_2_7144 target\n" +
+                "	USING sourceData2_7144 source\n" +
+                "	   ON target.CATEGORY = source.CATEGORY\n" +
+                "	  AND target.AGGDATE = source.AGGDATE\n" +
+                "	 WHEN MATCHED THEN\n" +
+                "	      UPDATE SET VALUE = target.VALUE + source.VALUE,\n" +
+                "	      ATTIME = CASE WHEN source.ATTIME < target.ATTIME THEN target.ATTIME ELSE source.ATTIME END,\n" +
+                "	      AGGCOUNT = target.AGGCOUNT + source.AGGCOUNT\n" +
+                "	 WHEN NOT MATCHED THEN\n" +
+                "	      INSERT (CATEGORY, VALUE, ATTIME, AGGDATE, AGGCOUNT)\n" +
+                "	      VALUES (source.CATEGORY, source.VALUE, source.ATTIME, source.AGGDATE, source.AGGCOUNT)\n"
+            );
+        assertResults
+            (
+                conn,
+                "SELECT * FROM targetData_2_7144",
+                new String[][]
+                {
+                    { "1", "1", "45.67", "2022-07-29 01:24:21.336", "2022-07-29", "3" },        
+                    { "2", "4", "37.15", "2022-07-29 01:24:21.336", "2022-07-29", "3" },         
+                    { "3", "2", "123.34", "2022-07-31 01:38:24.66", "2022-07-31", "3" },         
+                    { "4", "1", "78.9", "2022-07-31 01:38:24.66", "2022-07-31", "2" },          
+                    { "5", "3", "1.2", "2022-07-31 01:38:24.66",  "2022-07-31", "1" },         
+                    { "6", "4",  "5.6", "2022-07-31 01:38:24.66", "2022-07-31", "1" }          
+                },
+                false
+            );
+    }
+    
     ///////////////////////////////////////////////////////////////////////////////////
     //
     // ROUTINES