| /* |
| Derby - Class org.apache.derbyTesting.functionTests.tests.store.RowLockBasicTest |
| |
| 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.store; |
| |
| import java.sql.Connection; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.sql.Statement; |
| |
| import junit.framework.Test; |
| |
| import org.apache.derbyTesting.junit.BaseJDBCTestCase; |
| import org.apache.derbyTesting.junit.JDBC; |
| import org.apache.derbyTesting.junit.TestConfiguration; |
| |
| /** |
| * Very basic single user testing of row locking, verify that the right locks |
| * are obtained for simple operations. This test only looks at table and |
| * row logical locks, it does not verify physical latches or lock ordering. |
| * The basic methodology is: |
| * start transaction |
| * simple operation |
| * print lock table which should match the master |
| * end transation |
| * |
| */ |
| public class RowLockBasicTest extends BaseJDBCTestCase { |
| public RowLockBasicTest(String name) { |
| super(name); |
| } |
| |
| public static Test suite() { |
| return TestConfiguration.defaultSuite(RowLockBasicTest.class); |
| } |
| |
| protected void setUp() throws SQLException { |
| getConnection().setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); |
| Statement s = createStatement(); |
| s.execute("CREATE FUNCTION PADSTRING (DATA VARCHAR(32000), " |
| + "LENGTH INTEGER) RETURNS VARCHAR(32000) EXTERNAL NAME " |
| + "'org.apache.derbyTesting.functionTests.util.Formatters" |
| + ".padString' LANGUAGE JAVA PARAMETER STYLE JAVA"); |
| createLockTableQueryEntries(s); |
| |
| s.execute("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY" |
| + "('derby.storage.pageSize', '4096')"); |
| s.execute("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY" |
| + "('derby.storage.rowLocking', 'true')"); |
| |
| s.close(); |
| |
| getConnection().setAutoCommit(false); |
| |
| dropTable("a"); |
| } |
| |
| private void createLockTableQueryEntries(Statement s) throws SQLException { |
| s.execute("create view lock_table as " |
| + "select " |
| + "cast(username as char(8)) as username," |
| + "cast(t.type as char(8)) as trantype," |
| + "cast(l.type as char(8)) as type," |
| + "cast(lockcount as char(3)) as cnt," |
| + "mode," |
| + "cast(tablename as char(12)) as tabname," |
| + "cast(lockname as char(10)) as lockname," |
| + "state," |
| + "status " |
| + "from " |
| + "syscs_diag.lock_table l right outer join syscs_diag.transaction_table t " |
| + "on l.xid = t.xid where l.tableType <> 'S' and t.type='UserTransaction'"); |
| |
| //lock table with system catalog locks included. |
| s.execute("create view full_lock_table as " |
| + "select " |
| + "cast(username as char(8)) as username, " |
| + "cast(t.type as char(8)) as trantype," |
| + "cast(l.type as char(8)) as type," |
| + "cast(lockcount as char(3)) as cnt," |
| + "mode, " |
| + "cast(tablename as char(12)) as tabname," |
| + "cast(lockname as char(10)) as lockname," |
| + "state," |
| + "status " |
| + "from " |
| + "syscs_diag.lock_table l right outer join syscs_diag.transaction_table t " |
| + "on l.xid = t.xid where l.tableType <> 'S' "); |
| |
| //lock table with no join. |
| s.execute("create view lock_table2 as " |
| + "select " |
| + "cast(l.xid as char(8)) as xid," |
| + "cast(l.type as char(8)) as type," |
| + "cast(lockcount as char(3)) as cnt," |
| + "mode," |
| + "cast(tablename as char(12)) as tabname," |
| + "cast(lockname as char(10)) as lockname," |
| + "state " |
| + "from " |
| + "syscs_diag.lock_table l " |
| + "where l.tableType <> 'S' "); |
| |
| //-- transaction table with no join. |
| s.execute("create view tran_table as " |
| + "select " |
| + "* " |
| + "from " |
| + "syscs_diag.transaction_table"); |
| } |
| |
| public void tearDown() throws Exception { |
| Statement st = createStatement(); |
| st.executeUpdate("DROP FUNCTION PADSTRING"); |
| |
| st.execute("drop view lock_table"); |
| st.execute("drop view full_lock_table"); |
| st.execute("drop view lock_table2"); |
| st.execute("drop view tran_table"); |
| |
| st.close(); |
| dropTable("a"); |
| commit(); |
| |
| super.tearDown(); |
| } |
| |
| public void testInsertIntoHeap() throws SQLException { |
| Statement st = createStatement(); |
| st.execute("create table a (a int)"); |
| commit(); |
| |
| //Test insert into empty heap, should just get row lock on row being inserted |
| st.execute("insert into a values (1)"); |
| ResultSet rs = st.executeQuery( |
| " select * from lock_table order by tabname, type " |
| + "desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "IX", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "X", "A", "(1,7)", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| //Test insert into heap with one row, just get row lock on row being inserted |
| st.execute("insert into a values (2)"); |
| rs = st.executeQuery( |
| " select * from lock_table order by tabname, type " |
| + "desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "IX", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "X", "A", "(1,8)", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| dropTable("a"); |
| rs = st.executeQuery( |
| " select * from lock_table order by tabname, type " |
| + "desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "3", "X", "*** TRANSIEN", "Tablelock", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| st.close(); |
| } |
| |
| public void testInsertIntoBtree() throws SQLException { |
| Statement st = createStatement(); |
| |
| st.execute("create table a (a int, b varchar(1000))"); |
| ResultSet rs = st.executeQuery( |
| " select * from lock_table order by tabname, type " |
| + "desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "X", "A", "Tablelock", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| st.execute("create index a_idx on a (a, b)"); |
| rs = st.executeQuery( |
| " select * from lock_table order by tabname, type " |
| + "desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "4", "S", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "TABLE", "1", "X", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "TABLE", "1", "X", "A", "Tablelock", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| //Test insert into empty btree, should just get row lock on row being |
| //inserted and an instant duration lock on "first key in table" row (id 3). |
| st.execute("insert into a values (1, PADSTRING('a',1000))"); |
| rs = st.executeQuery( |
| " select * from lock_table order by tabname, type " |
| + "desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "2", "IX", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "X", "A", "(1,7)", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| //Test insert into non-empty btree, should get row lock on row being |
| //inserted and an instant duration lock on the one before it. |
| st.execute("insert into a values (2, PADSTRING('b',1000))"); |
| rs = st.executeQuery( |
| " select * from lock_table order by tabname, type " |
| + "desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "2", "IX", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "X", "A", "(1,8)", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| //Cause split and check locks that are obtained as part of inserting after |
| //the split. This causes the code to get a previous lock on a previous page. |
| |
| //RESOLVE (mikem) - the row lock on (1,9) is a result of raw store getting |
| //a lock while it tries to fit the row on the original page record id, but |
| // there is not enough room, so it eventually allocates a new page/row and |
| // locks that one - but the old lock is left around. |
| // |
| // btree just before commit: |
| // leftmost leaf: (1,6), (1,7) |
| // next leaf: (1,8), (2,6) |
| st.execute("insert into a values (3, PADSTRING('c',1000))"); |
| commit(); |
| st.execute("insert into a values (4, PADSTRING('d',1000))"); |
| |
| rs = st.executeQuery( |
| " select * from lock_table order by tabname, type " |
| + "desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "2", "IX", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "X", "A", "(1,10)", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "X", "A", "(2,6)", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| st.close(); |
| } |
| |
| /** |
| *Cause an insert on a new page that inserts into the 1st slot on the btree |
| *page. |
| * btree just before commit: |
| * leftmost leaf: (1,6), (1,7) |
| * next leaf: (2,7), (2,6) |
| */ |
| public void testInsertOnNewPage() throws SQLException { |
| Statement st = createStatement(); |
| st.execute("create table a (a int, b varchar(1000))"); |
| st.execute("create unique index a_idx on a (a, b)"); |
| st.execute("insert into a values (1, PADSTRING('a',1000))"); |
| st.execute("insert into a values (2, PADSTRING('b',1000))"); |
| st.execute("insert into a values (3, PADSTRING('c',1000))"); |
| st.execute("insert into a values (4, PADSTRING('d',1000))"); |
| |
| ResultSet rs = st.executeQuery("select a from a"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"1"}, |
| {"2"}, |
| {"3"}, |
| {"4"} |
| }); |
| |
| st.execute("delete from a where a = 3"); |
| rs = st.executeQuery("select a from a"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"1"}, |
| {"2"}, |
| {"4"} |
| }); |
| commit(); |
| |
| st.execute("insert into a values (3, PADSTRING('c',1000))"); |
| rs = st.executeQuery( |
| "select * from lock_table order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "2", "IX", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "X", "A", "(1,9)", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "X", "A", "(2,7)", "GRANT", "ACTIVE"} |
| }); |
| |
| st.close(); |
| } |
| |
| /** |
| * Do full covered index scan. |
| */ |
| public void testFullCoveredIndexScan() throws SQLException { |
| Statement st = createStatement(); |
| createTableAandUniqueIndex(st); |
| |
| ResultSet rs = st.executeQuery("select a from a"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"1"}, |
| {"2"}, |
| {"3"}, |
| {"4"} |
| }); |
| |
| rs = st.executeQuery("select * from lock_table order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "S", "A", "Tablelock", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| st.close(); |
| } |
| |
| private void createTableAandUniqueIndex(Statement st) throws SQLException { |
| st.execute("create table a (a int, b char(200))"); |
| st.execute("create unique index a_idx on a (a)"); |
| st.execute("insert into a values (1, 'a')"); |
| st.execute("insert into a values (2, 'b')"); |
| st.execute("insert into a values (3, 'c')"); |
| st.execute("insert into a values (4, 'd')"); |
| commit(); |
| } |
| |
| /** |
| * Do single probe into covered index (first key in table). |
| * |
| */ |
| public void testSingleProbeIntoFirstKey() throws SQLException { |
| Statement st = createStatement(); |
| createTableAandUniqueIndex(st); |
| |
| ResultSet rs = st.executeQuery("select a from a where a = 1"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"1"}, |
| }); |
| |
| rs = st.executeQuery("select * from lock_table order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "IS", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "S", "A", "(1,7)", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| st.close(); |
| } |
| |
| /** |
| * Do single probe into covered index (last key in table). |
| */ |
| public void testSingleProbeIntoLastKey() throws SQLException { |
| Statement st = createStatement(); |
| createTableAandUniqueIndex(st); |
| |
| ResultSet rs = st.executeQuery("select a from a where a = 4"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"4"}, |
| }); |
| |
| rs = st.executeQuery("select * from lock_table order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "IS", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "S", "A", "(1,10)", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| st.close(); |
| } |
| |
| /** |
| * Do set of range scans that all return 1 row from covered index. |
| */ |
| public void testSetOfRangeScanFor1RowReturn() throws SQLException { |
| Statement st = createStatement(); |
| createTableAandUniqueIndex(st); |
| |
| ResultSet rs = st.executeQuery("select a from a where a <= 1"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"1"} |
| }); |
| |
| rs = st.executeQuery("select * from lock_table order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "IS", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "S", "A", "(1,3)", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "S", "A", "(1,7)", "GRANT", "ACTIVE"} |
| }); |
| |
| commit(); |
| |
| |
| rs = st.executeQuery("select a from a where a >= 2 and a < 3"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"2"} |
| }); |
| |
| rs = st.executeQuery("select * from lock_table order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "IS", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "S", "A", "(1,8)", "GRANT", "ACTIVE"} |
| }); |
| |
| commit(); |
| |
| |
| rs = st.executeQuery("select a from a where a > 3"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"4"} |
| }); |
| |
| rs = st.executeQuery("select * from lock_table order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "IS", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "S", "A", "(1,10)", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "S", "A", "(1,9)", "GRANT", "ACTIVE"} |
| }); |
| |
| commit(); |
| |
| st.close(); |
| } |
| |
| /** |
| * Do set of range scans that all return 0 row from covered index. |
| */ |
| public void testSetOfRangeScanFor0RowReturn() throws SQLException { |
| Statement st = createStatement(); |
| createTableAandUniqueIndex(st); |
| |
| ResultSet rs = st.executeQuery("select a from a where a < 1"); |
| JDBC.assertDrainResults(rs, 0); |
| |
| rs = st.executeQuery("select * from lock_table order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "IS", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "S", "A", "(1,3)", "GRANT", "ACTIVE"} |
| }); |
| |
| commit(); |
| |
| |
| rs = st.executeQuery("select a from a where a > 4"); |
| JDBC.assertDrainResults(rs, 0); |
| |
| rs = st.executeQuery("select * from lock_table order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "IS", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "S", "A", "(1,10)", "GRANT", "ACTIVE"} |
| }); |
| |
| commit(); |
| |
| |
| rs = st.executeQuery("select a from a where a > 2 and a < 3"); |
| JDBC.assertDrainResults(rs, 0); |
| |
| rs = st.executeQuery("select * from lock_table order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "1", "IS", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "ROW", "1", "S", "A", "(1,8)", "GRANT", "ACTIVE"} |
| }); |
| |
| commit(); |
| |
| st.close(); |
| } |
| |
| /** |
| * Verify that create index does table level locking. |
| */ |
| public void testCreateIndexDoesTableLevelLocking() throws SQLException { |
| Statement st = createStatement(); |
| st.execute("create table a (a int, b char(200))"); |
| st.execute("create table b (a int, b char(200))"); |
| st.execute("insert into a values (1, 'a')"); |
| st.execute("insert into a values (2, 'b')"); |
| st.execute("insert into a values (3, 'c')"); |
| st.execute("insert into a values (4, 'd')"); |
| commit(); |
| |
| st.execute("create unique index a_idx on a (a)"); |
| |
| ResultSet rs = st.executeQuery("select * from lock_table " |
| + "order by tabname, type desc, mode, cnt, lockname"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"APP", "UserTran", "TABLE", "4", "S", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "TABLE", "1", "X", "A", "Tablelock", "GRANT", "ACTIVE"}, |
| {"APP", "UserTran", "TABLE", "1", "X", "A", "Tablelock", "GRANT", "ACTIVE"} |
| }); |
| commit(); |
| |
| rs = st.executeQuery("select a from a"); |
| JDBC.assertFullResultSet(rs, new String[][]{ |
| {"1"}, |
| {"2"}, |
| {"3"}, |
| {"4"} |
| }); |
| |
| rs = st.executeQuery("select a from b"); |
| JDBC.assertDrainResults(rs, 0); |
| |
| commit(); |
| |
| st.execute("drop index a_idx"); |
| dropTable("a"); |
| dropTable("b"); |
| |
| |
| st.close(); |
| } |
| } |