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 2012/05/02 17:55:32 UTC
svn commit: r1333087 - in /db/derby/code/branches/10.8: ./
java/client/org/apache/derby/client/net/
java/engine/org/apache/derby/impl/store/raw/data/
java/testing/org/apache/derbyTesting/functionTests/tests/lang/
Author: rhillegas
Date: Wed May 2 15:55:32 2012
New Revision: 1333087
URL: http://svn.apache.org/viewvc?rev=1333087&view=rev
Log:
DERBY-5679: Ported 1330877 and 1331484 from trunk to 10.8 branch.
Modified:
db/derby/code/branches/10.8/ (props changed)
db/derby/code/branches/10.8/java/client/org/apache/derby/client/net/NetCursor.java (props changed)
db/derby/code/branches/10.8/java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java
db/derby/code/branches/10.8/java/testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java
Propchange: db/derby/code/branches/10.8/
------------------------------------------------------------------------------
Merged /db/derby/code/trunk:r1330877,1331484
Propchange: db/derby/code/branches/10.8/java/client/org/apache/derby/client/net/NetCursor.java
------------------------------------------------------------------------------
Merged /db/derby/code/trunk/java/client/org/apache/derby/client/net/NetCursor.java:r1330877,1331484
Modified: db/derby/code/branches/10.8/java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.8/java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java?rev=1333087&r1=1333086&r2=1333087&view=diff
==============================================================================
--- db/derby/code/branches/10.8/java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java (original)
+++ db/derby/code/branches/10.8/java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java Wed May 2 15:55:32 2012
@@ -327,10 +327,15 @@ public class StoredPage extends CachedPa
* COLUMN_LONG - this is a known long column, therefore we will
* store part of the column on the current page and
* overflow the rest if necessary.
+ * COLUMN_CREATE_NULL - the column was recently added.
+ * it doesn't actually exist in the on-disk row yet.
+ * we will need to put a null in it as soon as possible.
+ * see DERBY-5679.
**/
protected static final int COLUMN_NONE = 0;
protected static final int COLUMN_FIRST = 1;
protected static final int COLUMN_LONG = 2;
+ protected static final int COLUMN_CREATE_NULL = 3;
/**
@@ -3968,12 +3973,14 @@ public class StoredPage extends CachedPa
else
{
// this is an update that is increasing the number of
- // columns but not providing any value, strange ...
-
+ // columns but not providing any value. this can happen
+ // if you are updating a new column after using
+ // ALTER TABLE to add a couple new columns.
+ // see DERBY-5679.
spaceAvailable =
logColumn(
null, 0, out, spaceAvailable,
- columnFlag, overflowThreshold);
+ COLUMN_CREATE_NULL, overflowThreshold);
}
}
@@ -6177,7 +6184,7 @@ public class StoredPage extends CachedPa
}
}
- if (column == null)
+ if ( (column == null) && (columnFlag != COLUMN_CREATE_NULL))
{
fieldStatus = StoredFieldHeader.setNonexistent(fieldStatus);
headerLength =
@@ -6300,11 +6307,28 @@ public class StoredPage extends CachedPa
}
}
+ else if ( columnFlag == COLUMN_CREATE_NULL )
+ {
+ //
+ // This block handles the case when a couple columns have been added
+ // recently and now one of the later columns is being updated. Newly added columns
+ // which appear in the row before the updated column don't actually have
+ // any values yet. We stuff NULLs into those newly added columns here.
+ // This fixes DERBY-5679.
+ //
+ fieldStatus = StoredFieldHeader.setNull(fieldStatus, true);
+
+ // header is written with 0 length here.
+ headerLength =
+ StoredFieldHeader.write(
+ logicalDataOut, fieldStatus,
+ fieldDataLength, slotFieldSize);
+ }
else if (column instanceof DataValueDescriptor)
{
DataValueDescriptor sColumn = (DataValueDescriptor) column;
- boolean isNull = sColumn.isNull();
+ boolean isNull = (columnFlag == COLUMN_CREATE_NULL) || sColumn.isNull();
if (isNull)
{
fieldStatus = StoredFieldHeader.setNull(fieldStatus, true);
Modified: db/derby/code/branches/10.8/java/testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.8/java/testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java?rev=1333087&r1=1333086&r2=1333087&view=diff
==============================================================================
--- db/derby/code/branches/10.8/java/testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java (original)
+++ db/derby/code/branches/10.8/java/testing/org/apache/derbyTesting/functionTests/tests/lang/AlterTableTest.java Wed May 2 15:55:32 2012
@@ -29,10 +29,9 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLWarning;
import java.sql.Connection;
-
-
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
+import java.util.Arrays;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.derbyTesting.functionTests.util.TestInputStream;
@@ -3729,4 +3728,217 @@ public final class AlterTableTest extend
s.execute("alter table \"\"\"\".\"\"\"\" " +
"alter column \"\"\"\" set increment by 2");
}
+
+ /**
+ * Verify that rollback works properly if a column with a null default
+ * is added and then the table is updated. See DERBY-5679.
+ */
+ public void test_5679() throws Exception
+ {
+ Statement s = createStatement();
+ ResultSet rs;
+
+ String[][] rowBefore = new String[][]{ { "before", null, "before" } };
+ String[][] rowAfter = new String[][]{ { "after", "after", "after" } };
+
+ // create a table, insert a row, add two columns, then update one of the columns
+ s.execute( "create table t_5679(name1 varchar(10))" );
+ s.execute( "insert into t_5679(name1) values('before')" );
+ s.execute( "alter table t_5679 add column str1 varchar(10)" );
+ s.execute( "alter table t_5679 add column str2 varchar(10)" );
+ s.execute( "update t_5679 set str2 = 'before'" );
+
+ rs = s.executeQuery( "select * from t_5679" );
+ JDBC.assertFullResultSet( rs, rowBefore );
+
+ // now update the row and rollback
+ setAutoCommit( false );
+ s.execute( "update t_5679 set name1='after', str1='after', str2='after'" );
+ rs = s.executeQuery( "select * from t_5679" );
+ JDBC.assertFullResultSet( rs, rowAfter );
+ rollback();
+ setAutoCommit( true );
+
+ // all columns of the row should have reverted
+ rs = s.executeQuery( "select * from t_5679" );
+ JDBC.assertFullResultSet( rs, rowBefore );
+
+ s.execute( "drop table t_5679" );
+ }
+
+ /**
+ * More tests for DERBY-5679. Verify with a lot of columns.
+ */
+ public void test_5679_manyColumns() throws Exception
+ {
+ Statement s = createStatement();
+ ResultSet rs;
+
+ // create a table, insert a row, add two columns, then update one of the columns
+ s.execute( "create table t_5679_1( keyCol int )" );
+ s.execute( "insert into t_5679_1( keyCol ) values( 1 )" );
+
+ // now add a lot of columns
+ for ( int i = 1; i < 100; i++ )
+ {
+ s.execute( "alter table t_5679_1 add column a_" + i + " int" );
+ }
+ s.execute( "update t_5679_1 set a_50 = 50" );
+
+ String[] rawBeforeRow = new String[ 100 ];
+ rawBeforeRow[ 0 ] = "1";
+ rawBeforeRow[ 50 ] = "50";
+ String[][] beforeRow = new String[][] { rawBeforeRow };
+
+ String[] rawAfterRow = new String[ 100 ];
+ rawAfterRow[ 0 ] = "1";
+ rawAfterRow[ 49 ] = "490";
+ rawAfterRow[ 50 ] = "500";
+ rawAfterRow[ 51 ] = "510";
+ String[][] afterRow = new String[][] { rawAfterRow };
+
+ rs = s.executeQuery( "select * from t_5679_1" );
+ JDBC.assertFullResultSet( rs, beforeRow );
+
+ // now update the row and rollback
+ setAutoCommit( false );
+ s.execute( "update t_5679_1 set a_49 = 490, a_50 = 500, a_51 = 510" );
+ rs = s.executeQuery( "select * from t_5679_1" );
+ JDBC.assertFullResultSet( rs, afterRow );
+ rollback();
+ setAutoCommit( true );
+
+ // all columns of the row should have reverted
+ rs = s.executeQuery( "select * from t_5679_1" );
+ JDBC.assertFullResultSet( rs, beforeRow );
+
+ s.execute( "drop table t_5679_1" );
+ }
+
+ /**
+ * More tests for DERBY-5679. Verify with long rows.
+ */
+ public void test_5679_longRows() throws Exception
+ {
+ Connection conn = getConnection();
+ PreparedStatement ps;
+ ResultSet rs;
+
+ // verify that the default page size of 4096 bytes is in effect
+ ps = conn.prepareStatement( "values syscs_util.syscs_get_database_property( 'derby.storage.pageSize' )" );
+ rs = ps.executeQuery();
+ rs.next();
+ assertNull( rs.getString( 1 ) );
+ rs.close();
+ ps.close();
+
+ final int LONG = 1050;
+ final int SHORT = 500;
+ final int PAGE_SIZE = 4096;
+
+ byte[] a_0 = makeBytes( 0, LONG );
+ byte[] a_1 = makeBytes( 1, LONG );
+ byte[] a_2 = makeBytes( 2, LONG );
+ byte[] a_4 = makeBytes( 4, LONG );
+
+ // create a table, insert a row, add two columns, then update one of the columns
+ conn.prepareStatement
+ (
+ "create table t_5679_2( a_0 varchar( " + LONG + " ) for bit data," +
+ " a_1 varchar( " + LONG + " ) for bit data," +
+ " a_2 varchar( " + LONG + " ) for bit data)" )
+ .execute();
+ ps = conn.prepareStatement( "insert into t_5679_2( a_0, a_1, a_2 ) values ( ?, ?, ? )" );
+ ps.setBytes( 1, a_0 );
+ ps.setBytes( 2, a_1 );
+ ps.setBytes( 3, a_2 );
+ ps.executeUpdate();
+ ps.close();
+
+ // now add 2 columns. the second column will spill onto the second page if it is
+ // stuffed with a long value
+ conn.prepareStatement( "alter table t_5679_2 add column a_3 varchar( " + SHORT + " ) for bit data" ).execute();
+ conn.prepareStatement( "alter table t_5679_2 add column a_4 varchar( " + LONG + " ) for bit data" ).execute();
+
+ assertTrue( LONG + LONG + LONG + SHORT < PAGE_SIZE );
+ assertTrue( LONG + LONG + LONG + LONG > PAGE_SIZE );
+
+ // now stuff the second newly added column with a large value which
+ // spills onto the next page
+ ps = conn.prepareStatement( "update t_5679_2 set a_4 = ?" );
+ ps.setBytes( 1, a_4 );
+ ps.executeUpdate();
+ ps.close();
+
+
+ byte[] after_0 = makeBytes( 100, LONG );
+ byte[] after_1 = makeBytes( 101, LONG );
+ byte[] after_2 = makeBytes( 102, LONG );
+ byte[] after_3 = makeBytes( 103, SHORT );
+ byte[] after_4 = makeBytes( 104, LONG );
+
+ byte[][] beforeRow = new byte[][] { a_0, a_1, a_2, null, a_4 };
+ byte[][] afterRow = new byte[][] { after_0, after_1, after_2, after_3, after_4 };
+
+ vetBytes_5679( conn, beforeRow );
+
+ // now update the row and rollback. columns a_0 through a_3 should stay on
+ // the first page and a_4 should stay on the second page
+ conn.setAutoCommit( false );
+ ps = conn.prepareStatement( "update t_5679_2 set a_0 = ?, a_1 = ?, a_2 = ?, a_3 = ?, a_4 = ?" );
+ ps.setBytes( 1, after_0 );
+ ps.setBytes( 2, after_1 );
+ ps.setBytes( 3, after_2 );
+ ps.setBytes( 4, after_3 );
+ ps.setBytes( 5, after_4 );
+ ps.executeUpdate();
+ vetBytes_5679( conn, afterRow );
+ rollback();
+ conn.setAutoCommit( true );
+
+ // all columns of the row should have reverted
+ vetBytes_5679( conn, beforeRow );
+
+ conn.prepareStatement( "drop table t_5679_2" ).execute();
+ }
+ private byte[] makeBytes( int seed, int length )
+ {
+ byte[] result = new byte[ length ];
+
+ Arrays.fill( result, (byte) seed );
+
+ return result;
+ }
+ private void vetBytes_5679( Connection conn, byte[][] expected ) throws Exception
+ {
+ PreparedStatement ps = conn.prepareStatement( "select * from t_5679_2" );
+ ResultSet rs = ps.executeQuery();
+
+ rs.next();
+
+ for ( int i = 0; i < expected.length; i++ )
+ {
+ assertBytes( expected[ i ] , rs.getBytes( i + 1 ) );
+ }
+
+ rs.close();
+ ps.close();
+ }
+ private void assertBytes( byte[] expected, byte[] actual ) throws Exception
+ {
+ if ( expected == null )
+ {
+ assertNull( actual );
+ return;
+ }
+ else { assertNotNull( actual ); }
+
+ assertEquals( expected.length, actual.length );
+
+ for ( int i = 0; i < expected.length; i++ )
+ {
+ assertEquals( expected[ i ], actual[ i ] );
+ }
+ }
+
}