blob: a6723f52af5d8a28cf201bee1201380f3b91b009 [file] [log] [blame]
/**
* Derby - Class org.apache.derbyTesting.functionTests.tests.lang.UpdateCursorTest
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.derbyTesting.functionTests.tests.lang;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.Properties;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
import org.apache.derbyTesting.junit.SystemPropertyTestSetup;
/**
* This tests updateable cursor using index, Beetle entry 3865.
*
* Not done in ij since we need to do many "next" and "update" to be able to
* excercise the code of creating temp conglomerate for virtual memory heap. We
* need at minimum 200 rows in table, if "maxMemoryPerTable" property is set to
* 1 (KB). This includes 100 rows to fill the in-memory portion of the hash
* table, and another 100 rows to fill an in-memory heap that was used until
* DERBY-5425 removed it.
*/
public class UpdateCursorTest extends BaseJDBCTestCase {
private static final int SIZE_OF_T1 = 250;
private static final String EXPECTED_SQL_CODE = "02000";
/**
* Basic constructor.
*/
public UpdateCursorTest(String name) {
super(name);
}
/**
* Sets the auto commit to false.
*/
protected void initializeConnection(Connection conn) throws SQLException {
conn.setAutoCommit(false);
}
/**
* Returns the implemented tests.
*
* @return An instance of <code>Test</code> with the implemented tests to
* run.
*/
public static Test suite() {
Properties props = new Properties();
props.setProperty("derby.language.maxMemoryPerTable", "1");
return new DatabasePropertyTestSetup(
new SystemPropertyTestSetup(
new CleanDatabaseTestSetup(
new BaseTestSuite(
UpdateCursorTest.class, "UpdateCursorTest")) {
/**
* @see org.apache.derbyTesting.junit.CleanDatabaseTestSetup#decorateSQL(java.sql.Statement)
*/
protected void decorateSQL(Statement s) throws SQLException {
StringBuffer sb = new StringBuffer(1000);
String largeString;
PreparedStatement pstmt;
assertUpdateCount(s, 0, "create table T1 ("
+ " c1 int," + " c2 char(50)," + " c3 int," + " c4 char(50),"
+ " c5 int," + " c6 varchar(1000))");
assertUpdateCount(s, 0, "create index I11 on T1(c3, c1, c5)");
assertUpdateCount(s, 0, "create table T2("
+ " c1 int)");
assertUpdateCount(s, 0, "create table T3("
+ " c1 char(20) not null primary key)");
assertUpdateCount(s, 0, "create table T4("
+ " c1 char(20) references T3(c1) on delete cascade)");
/* fill the newly created tables */
for (int i = 0; i < 1000; i++) {
sb.append('a');
}
pstmt = s.getConnection().prepareStatement("insert into T1 values (?, ?, ?, ?, ?, ?), " +
"(?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?), "
+ "(?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?)");
largeString = new String(sb);
for (int i = 246; i > 0; i = i - 5) {
int k = 0;
for (int j = 0; j < 5; j++) {
pstmt.setInt(1 + k, i + (4 - j));
pstmt.setString(2 + k, Integer.toString(i));
pstmt.setInt(3 + k, i + j);
pstmt.setString(4 + k, Integer.toString(i));
pstmt.setInt(5 + k, i);
pstmt.setString(6 + k, largeString);
k += 6;
}
assertUpdateCount(pstmt, 5);
}
s.executeUpdate("insert into t2 values (1)");
pstmt.close();
}
}, props), props, true);
}
/**
* Test the virtual memory heap.
*
* @throws SQLException
*/
public void testVirtualMemoryHeap() throws SQLException {
PreparedStatement select = prepareStatement("select c1, c3 from t1 where c3 > 1 and c1 > 0 for update");
Statement update = createStatement();
String cursorName;
ResultSet cursor;
int expectedValue = 1;
/* drop index and recreate it to be sure that it is ascending
* (other subtests may have changed it)
*/
assertUpdateCount(update, 0, "drop index I11");
assertUpdateCount(update, 0, "create index I11 on T1 (c3, c1, c5)");
cursor = select.executeQuery(); // cursor is now open
cursorName = cursor.getCursorName();
/* scan the entire table except the last row. */
for (int i = 0; i < SIZE_OF_T1 - 1; i++) {
// Expect the values to be returned in index order.
expectedValue++;
assertEquals(cursor.next(), true);
//System.out.println("Row " + i + ": "+cursor.getInt(1)+","+cursor.getInt(2)+": "+expectedValue);
assertEquals("Virtual memory heap test failed! Got unexpected value.", expectedValue, cursor.getInt(2));
update.execute("update t1 set c3 = c3 + 250 where current of " + cursorName);
}
assertFalse(
"Update with virtual memory heap failed! Still got rows.",
cursor.next());
cursor.close();
update.close();
/* see what we have in the table */
select = prepareStatement("select c1, c3 from t1");
cursor = select.executeQuery(); // cursor is now open
for (int i = 0; i < SIZE_OF_T1; i++) {
assertEquals(cursor.next(), true);
}
assertFalse(
"Update with virtual memory heeap failed! Got more rows.",
cursor.next());
select.close();
cursor.close();
rollback();
}
/**
* Tests non covering index.
*
* @throws SQLException
*/
public void testNonCoveringIndex() throws SQLException {
PreparedStatement select;
Statement update;
ResultSet cursor;
String cursorName;
update = createStatement();
select = prepareStatement("select c3, c2 from t1 where c3 > 125 and c1 > 0 for update");
cursor = select.executeQuery(); // cursor is now open
cursorName = cursor.getCursorName();
for (int i = 0; i < (SIZE_OF_T1 / 2); i++) {
assertEquals(cursor.next(), true);
update.execute("update t1 set c3 = c3 + 25 where current of " + cursorName);
}
assertFalse(
"Update using noncovering index failed! Still got rows.",
cursor.next());
cursor.close();
select.close();
/* see what we have in the table */
select = prepareStatement("select c1, c3 from t1");
cursor = select.executeQuery(); // cursor is now open
for (int i = 0; i < SIZE_OF_T1; i++) {
assertEquals(cursor.next(), true);
}
assertFalse(
"Update using noncovering index failed! Got more rows.", cursor
.next());
select.close();
cursor.close();
rollback();
}
/**
* Tests descending index.
*
* @throws SQLException
*/
public void testDescendingIndex() throws SQLException {
PreparedStatement select;
Statement update;
ResultSet cursor;
update = createStatement();
/* drop index and recreate it */
assertUpdateCount(update, 0, "drop index I11");
assertUpdateCount(update, 0, "create index I11 on T1 (c3 desc, c1, c5 desc)");
commit();
update = createStatement();
select = prepareStatement("select c3, c1 from t1 where c3 > 125 and c1 > 0 for update");
cursor = select.executeQuery(); // cursor is now open
for (int i = 0; i < (SIZE_OF_T1 / 2); i++) {
assertEquals(cursor.next(), true);
if ((i % 2) == 0) {
update.execute("update t1 set c3 = c3 + 1 where current of " + cursor.getCursorName());
} else {
update.execute("update t1 set c3 = c3 - 1 where current of " + cursor.getCursorName());
}
}
assertFalse("Update using desc index failed! Got more rows.",
cursor.next());
cursor.close();
select.close();
/* see what we have in the table */
select = prepareStatement("select c3, c2 from t1");
cursor = select.executeQuery(); // cursor is now open
for (int i = 0; i < SIZE_OF_T1; i++) {
assertEquals(cursor.next(), true);
}
assertFalse("Update using desc index failed! Got more rows.",
cursor.next());
select.close();
cursor.close();
rollback();
}
/**
* Test if the correct warnings are raised.
*
* @throws SQLException
*/
public void testUpdateDeleteWarning() throws SQLException {
Statement stmt = createStatement();
SQLWarning sw;
stmt.executeUpdate("update t2 set c1 = 2 where c1 = 1");
sw = stmt.getWarnings();
assertNull("The update should not return a warning.", sw);
stmt.executeUpdate("update t2 set c1 = 2 where c1 = 1");
sw = stmt.getWarnings();
assertNotNull("The update should return a warning.", sw);
assertEquals("Wrong sql state.", EXPECTED_SQL_CODE, sw
.getSQLState());
stmt.executeUpdate("delete from t2 where c1 = 2");
sw = stmt.getWarnings();
assertNull("The delete should not return a warning.", sw);
stmt.executeUpdate("delete from t2 where c1 = 2");
sw = stmt.getWarnings();
assertNotNull("The delete should return a warning.", sw);
assertEquals("Wrong sql state.", EXPECTED_SQL_CODE, sw
.getSQLState());
stmt.executeUpdate("delete from t3");
sw = stmt.getWarnings();
assertNotNull("The delete cascade should return a warning.", sw);
assertEquals("Wrong sql state.", EXPECTED_SQL_CODE, sw
.getSQLState());
stmt.close();
rollback();
}
/**
* Regression test case for DERBY-5425. The scan used to lose rows that
* had spilt to disk from the data structure that keeps track of already
* seen rows, if the transaction was committed in the middle of the scan.
*/
public void testDerby5425HoldOverCommit() throws SQLException {
Statement stmt = createStatement();
// Drop index and recreate it to be sure that it is ascending
// (other subtests may have changed it)
assertUpdateCount(stmt, 0, "drop index I11");
assertUpdateCount(stmt, 0, "create index I11 on T1 (c3, c1, c5)");
PreparedStatement sel = prepareStatement(
"select c3 from t1 --DERBY-PROPERTIES index=I11",
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = sel.executeQuery();
for (int i = 1; i <= SIZE_OF_T1; i++) {
assertTrue("Too few rows", rs.next());
assertEquals(i, rs.getInt(1));
rs.updateInt(1, i);
rs.updateRow();
commit();
}
assertFalse("Too many rows", rs.next());
rs.close();
}
}