001: /*
002:
003: Derby - Class org.apache.derbyTesting.functionTests.harness.procedure
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derbyTesting.functionTests.tests.store;
023:
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025:
026: import java.sql.CallableStatement;
027: import java.sql.Connection;
028: import java.sql.PreparedStatement;
029: import java.sql.ResultSet;
030: import java.sql.SQLException;
031: import java.sql.Statement;
032:
033: import org.apache.derby.tools.ij;
034:
035: /**
036:
037: The purpose of this test is to reproduce JIRA DERBY-662:
038:
039: Sometimes during redo the system would incorrectly remove the file associated
040: with a table. The bug required the following conditions to reproduce:
041: 1) The OS/filesystem must be case insensitive such that a request to delete
042: a file named C2080.dat would also remove c2080.dat. This is true in
043: windows default file systems, not true in unix/linux filesystems that
044: I am aware of.
045: 2) The system must be shutdown not in a clean manner, such that a subsequent
046: access of the database causes a REDO recovery action of a drop table
047: statement. This means that a drop table statement must have happened
048: since the last checkpoint in the log file. Examples of things that cause
049: checkpoints are:
050: o clean shutdown from ij using the "exit" command
051: o clean shutdown of database using the "shutdown=true" url
052: o calling the checkpoint system procedure
053: o generating enough log activity to cause a regularly scheduled checkpoint.
054: 3) If the conglomerate number of the above described drop table is TABLE_1,
055: then for a problem to occur there must also exist in the database a table
056: such that it's HEX(TABLE_2) = TABLE_1
057: 4) Either TABLE_2 must not be accessed during REDO prior to the REDO operation
058: of the drop of TABLE_1 or there must be enough other table references during
059: the REDO phase to push the caching of of the open of TABLE_2 out of cache.
060:
061: If all of the above conditions are met then during REDO the system will
062: incorrectly delete TABLE_2 while trying to redo the drop of TABLE_1.
063: <p>
064: This test reproduces the problem by doing the following:
065: 1) create 500 tables, need enough tables to insure that conglomerate number
066: 2080 (c820.dat) and 8320 (c2080.dat) exist.
067: 2) checkpoint the database so that create does not happen during REDO
068: 3) drop table with conglomerate number 2080, mapping to c820.dat. It looks
069: it up in the catalog in case conglomerate number assignment changes for
070: some reason.
071: 4) exit the database without a clean shudown, this is the default for test
072: suites which run multiple tests in a single db - no clean shutdown is done.
073: Since we only do a single drop since the last checkpoint, test will cause
074: the drop during the subsequent REDO.
075: 5) run next test program dropcrash2, which will cause redo of the drop. At
076: this point the bug will cause file c2080.dat to be incorrectly deleted and
077: thus accesses to conglomerate 8320 will throw container does not exist
078: errors.
079: 6) check the consistency of the database which will find the container does
080: not exist error.
081:
082: **/
083:
084: public class dropcrash extends BaseTest {
085: boolean verbose = false;
086:
087: public dropcrash() {
088: }
089:
090: /**
091: * create tables, commit, and cause checkpoint of db.
092: **/
093: public void drop_crash_setup(Connection conn, int num_create)
094: throws SQLException {
095: beginTest(conn, "creating " + num_create + " tables.");
096: String create_stmt_str1 = "create table dropcrash_";
097: String create_stmt_str2 = " (a int)";
098:
099: for (int i = 0; i < num_create; i++) {
100: executeQuery(conn, create_stmt_str1 + i + create_stmt_str2,
101: false);
102: }
103: conn.commit();
104:
105: // during redo insure that drop is the only thing redone, if there
106: // are other files in the open file cache then bug will not reproduce
107: // because delete on the open file will fail.
108: executeQuery(conn,
109: "CALL SYSCS_UTIL.SYSCS_CHECKPOINT_DATABASE()", false);
110:
111: endTest(conn, "creating " + num_create + " tables.");
112: }
113:
114: /**
115: * Reproduce JIRA DERBY-662
116: * <p>
117: * Find the conglomerate with number 2080, and drop it. The bug is
118: * that during redo the system, on windows, will incorrectly delete
119: * C2080.dat because it did not do the hex conversion on the conglomerate
120: * number. This will result in conglomerate 8320 not having it's
121: * associate data file c2080.dat.
122: *
123: * @exception StandardException Standard exception policy.
124: **/
125: public void drop_crash_drop_table(Connection conn)
126: throws SQLException {
127: beginTest(conn, "dropping table with conglomerate number 2080.");
128: PreparedStatement ps = conn
129: .prepareStatement("select sys.systables.tablename, sys.sysconglomerates.conglomeratenumber from sys.systables, sys.sysconglomerates where sys.systables.tableid = sys.sysconglomerates.tableid and sys.systables.schemaid = sys.sysconglomerates.schemaid and sys.sysconglomerates.conglomeratenumber = ?");
130: ps.setInt(1, 2080);
131: ResultSet rs = ps.executeQuery();
132:
133: if (!rs.next()) {
134: System.out
135: .println("ERROR, did not find conglomerate to drop");
136: }
137: String drop_name = rs.getString(1);
138:
139: // don't print table name out to test output as it could change if
140: // other recovery tests are added, or system catalogs are added.
141: // System.out.println("dropping table:" + drop_name + " with conglomerate number " + rs.getInt(2));
142: executeQuery(conn, "drop table " + drop_name, false);
143: conn.commit();
144:
145: // at this point it is important for this test to exit with not a
146: // clean shutdown, so that the next test will force recovery redo
147: // of this drop.
148: endTest(conn, "dropping table with conglomerate number 2080.");
149: }
150:
151: public void testList(Connection conn) throws SQLException {
152: // create enough tables to insure congloms 2080 and 8320 exist
153: drop_crash_setup(conn, 500);
154: // drop 2080 and exit program so that drop will be in REDO recovery
155: drop_crash_drop_table(conn);
156: }
157:
158: public static void main(String[] argv) throws Throwable {
159: dropcrash test = new dropcrash();
160:
161: ij.getPropertyArg(argv);
162: Connection conn = ij.startJBMS();
163: conn.setAutoCommit(false);
164:
165: try {
166: test.testList(conn);
167: } catch (SQLException sqle) {
168: org.apache.derby.tools.JDBCDisplayUtil.ShowSQLException(
169: System.out, sqle);
170: sqle.printStackTrace(System.out);
171: }
172: }
173: }
|