/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.test.it.dxl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertNull; import java.util.Arrays; import java.util.List; import com.foundationdb.ais.model.AkibanInformationSchema; import com.foundationdb.ais.model.TableName; import com.foundationdb.qp.operator.Cursor; import com.foundationdb.qp.operator.StoreAdapter; import com.foundationdb.qp.row.Row; import com.foundationdb.qp.rowtype.RowType; import com.foundationdb.qp.rowtype.Schema; import com.foundationdb.qp.util.SchemaCache; import com.foundationdb.server.test.it.ITBase; import com.foundationdb.server.test.it.qp.TestRow; import org.junit.Test; import com.foundationdb.ais.model.Column; import com.foundationdb.ais.model.Group; import com.foundationdb.ais.model.PrimaryKey; import com.foundationdb.ais.model.Table; import com.foundationdb.server.error.InvalidOperationException; import com.foundationdb.server.error.UnsupportedDropException; public final class COIBasicIT extends ITBase { private static class TableIds { public final int c; public final int o; public final int i; private TableIds(int c, int o, int i) { this.c = c; this.o = o; this.i = i; } } private TableIds createTables() throws InvalidOperationException { int cId = createTable("coi", "c", "cid int not null primary key, name varchar(32)"); int oId = createTable("coi", "o", "oid int not null primary key, c_id int, GROUPING FOREIGN KEY (c_id) REFERENCES c(cid)"); createIndex("coi", "o", "__akiban_fk_o", "c_id"); int iId = createTable("coi", "i", "iid int not null primary key, o_id int, idesc varchar(32), GROUPING FOREIGN KEY (o_id) REFERENCES o(oid)"); createIndex("coi", "i", "__akiban_fk_i", "o_id"); AkibanInformationSchema ais = ddl().getAIS(session()); // Lots of checking, the more the merrier final Table cTable = ais.getTable( ddl().getTableName(session(), cId) ); { assertEquals("c.columns.size()", 2, cTable.getColumns().size()); assertEquals("c.indexes.size()", 1, cTable.getIndexes().size()); PrimaryKey pk = cTable.getPrimaryKey(); List<Column> expectedPkCols = Arrays.asList( cTable.getColumn("cid") ); assertEquals("pk cols", expectedPkCols, pk.getColumns()); assertSame("pk index", cTable.getIndex("PRIMARY"), pk.getIndex()); assertEquals("pk index cols size", 1, pk.getIndex().getKeyColumns().size()); assertEquals("parent join", null, cTable.getParentJoin()); assertEquals("child joins.size", 1, cTable.getChildJoins().size()); } final Table oTable = ais.getTable( ddl().getTableName(session(), oId) ); { assertEquals("c.columns.size()", 2, oTable.getColumns().size()); assertEquals("c.indexes.size()", 2, oTable.getIndexes().size()); PrimaryKey pk = oTable.getPrimaryKey(); List<Column> expectedPkCols = Arrays.asList( oTable.getColumn("oid") ); assertEquals("pk cols", expectedPkCols, pk.getColumns()); assertSame("pk index", oTable.getIndex("PRIMARY"), pk.getIndex()); assertEquals("pk index cols size", 1, pk.getIndex().getKeyColumns().size()); assertNotNull("parent join is null", oTable.getParentJoin()); assertSame("parent join", cTable.getChildJoins().get(0), oTable.getParentJoin()); assertEquals("child joins.size", 1, oTable.getChildJoins().size()); } final Table iTable = ais.getTable( ddl().getTableName(session(), iId) ); { assertEquals("c.columns.size()", 3, iTable.getColumns().size()); assertEquals("c.indexes.size()", 2, iTable.getIndexes().size()); PrimaryKey pk = iTable.getPrimaryKey(); List<Column> expectedPkCols = Arrays.asList( iTable.getColumn("iid") ); assertEquals("pk cols", expectedPkCols, pk.getColumns()); assertSame("pk index", iTable.getIndex("PRIMARY"), pk.getIndex()); assertEquals("pk index cols size", 1, pk.getIndex().getKeyColumns().size()); assertNotNull("parent join is null", iTable.getParentJoin()); assertSame("parent join", oTable.getChildJoins().get(0), iTable.getParentJoin()); assertEquals("child joins.size", 0, iTable.getChildJoins().size()); } { Group group = cTable.getGroup(); assertSame("o's group", group, oTable.getGroup()); assertSame("i's group", group, iTable.getGroup()); } return new TableIds(cId, oId, iId); } @Test public void simple() { createTables(); } @Test public void insertToUTablesAndScan() { final TableIds tids = createTables(); final Row cRow = row(tids.c, 1, "Robert"); final Row oRow = row(tids.o, 10, 1); final Row iRow = row(tids.i, 100, 10, "Desc 1"); writeRows(cRow); writeRows(oRow); writeRows(iRow); expectRows(tids.c, cRow); expectRows(tids.o, oRow); expectRows(tids.i, iRow); } @Test(expected=UnsupportedDropException.class) public void dropTableRoot() throws InvalidOperationException { final TableIds tids = createTables(); ddl().dropTable(session(), tableName(tids.c)); } @Test(expected=UnsupportedDropException.class) public void dropTableMiddle() throws InvalidOperationException { final TableIds tids = createTables(); ddl().dropTable(session(), tableName(tids.o)); } @Test public void dropTableLeaves() throws InvalidOperationException { final TableIds tids = createTables(); final Row cRow = row(tids.c, 1, "Robert"); final Row oRow = row(tids.o, 10, 1); final Row iRow = row(tids.i, 100, 10, "Desc 1"); writeRows(cRow, oRow, iRow); expectRows(tids.c, cRow); expectRows(tids.o, oRow); expectRows(tids.i, iRow); ddl().dropTable(session(), tableName(tids.i)); expectRows(tids.c, cRow); expectRows(tids.o, oRow); ddl().dropTable(session(), tableName(tids.o)); expectRows(tids.c, cRow); ddl().dropTable(session(), tableName(tids.c)); } @Test public void dropAllTablesHelper() throws InvalidOperationException { createTables(); createTable("test", "parent", "id int not null primary key"); createTable("test", "child", "id int not null primary key, pid int, GROUPING FOREIGN KEY (pid) REFERENCES parent(id)"); dropAllTables(); } @Test public void dropGroup() throws InvalidOperationException { final TableIds tids = createTables(); final TableName groupName = ddl().getAIS(session()).getTable(tableName(tids.i)).getGroup().getName(); ddl().dropGroup(session(), groupName); AkibanInformationSchema ais = ddl().getAIS(session()); assertNull("expected no table", ais.getTable("coi", "c")); assertNull("expected no table", ais.getTable("coi", "o")); assertNull("expected no table", ais.getTable("coi", "i")); assertNull("expected no group", ais.getGroup(groupName)); } @Test public void writeRowHKeyChangePropagation() { final TableIds tids = createTables(); Schema schema = SchemaCache.globalSchema(ddl().getAIS(session())); RowType cType = schema.tableRowType(getTable(tids.c)); RowType oType = schema.tableRowType(getTable(tids.o)); RowType iType = schema.tableRowType(getTable(tids.i)); StoreAdapter adapter = newStoreAdapter(); Object[] o1Cols = { 10, 1 }; Object[] cCols = { 2, "c2" }; Object[] oCols = { 20, 2 }; Object[] iCols = { 200, 20, "i200" }; TestRow o1Row = new TestRow(oType, o1Cols); TestRow cRow = new TestRow(cType, cCols); TestRow oRow = new TestRow(oType, oCols); TestRow iRow = new TestRow(iType, iCols); // Unrelated o row, to demonstrate i ordering/adoption writeRow(tids.o, o1Cols); compareRows( new Row[] { o1Row }, adapter.newGroupCursor(cType.table().getGroup()) ); // i is first due to null cid component writeRow(tids.i, iCols); compareRows( new Row[] { iRow, o1Row }, adapter.newGroupCursor(cType.table().getGroup()) ); // i should get adopted by the new o, filling in it's cid component writeRow(tids.o, oCols); compareRows( new Row[] { o1Row, oRow, iRow, }, adapter.newGroupCursor(cType.table().getGroup()) ); writeRow(tids.c, cCols); compareRows( new Row[] { o1Row, cRow, oRow, iRow }, adapter.newGroupCursor(cType.table().getGroup()) ); } @Test public void deleteRowHKeyChangePropagation() { final TableIds tids = createTables(); Schema schema = SchemaCache.globalSchema(ddl().getAIS(session())); RowType cType = schema.tableRowType(getTable(tids.c)); RowType oType = schema.tableRowType(getTable(tids.o)); RowType iType = schema.tableRowType(getTable(tids.i)); StoreAdapter adapter = newStoreAdapter(); Object[] o1Cols = { 10, 1 }; Object[] cCols = { 2, "c2" }; Object[] oCols = { 20, 2 }; Object[] iCols = { 200, 20, "i200" }; TestRow o1Row = new TestRow(oType, o1Cols); TestRow cRow = new TestRow(cType, cCols); TestRow oRow = new TestRow(oType, oCols); TestRow iRow = new TestRow(iType, iCols); writeRow(tids.o, o1Cols); writeRow(tids.c, cCols); writeRow(tids.o, oCols); writeRow(tids.i, iCols); compareRows( new Row[] { o1Row, cRow, oRow, iRow }, adapter.newGroupCursor(cType.table().getGroup()) ); deleteRow(tids.c, cCols); compareRows( new Row[] { o1Row, oRow, iRow }, adapter.newGroupCursor(cType.table().getGroup()) ); // Delete o => i.cid becomes null deleteRow(tids.o, oCols); compareRows( new Row[] { iRow, o1Row }, adapter.newGroupCursor(cType.table().getGroup()) ); deleteRow(tids.i, iCols); compareRows( new Row[] { o1Row }, adapter.newGroupCursor(cType.table().getGroup()) ); deleteRow(tids.o, o1Cols); compareRows( new Row[] { }, adapter.newGroupCursor(cType.table().getGroup()) ); } @Test public void updateRowHKeyChangePropagation() { final TableIds tids = createTables(); Schema schema = SchemaCache.globalSchema(ddl().getAIS(session())); RowType cType = schema.tableRowType(getTable(tids.c)); RowType oType = schema.tableRowType(getTable(tids.o)); RowType iType = schema.tableRowType(getTable(tids.i)); StoreAdapter adapter = newStoreAdapter(); Object[] o1Cols = { 10, 1 }; Object[] cCols = { 2, "c2" }; Object[] oOrig = { 1, 1 }; Object[] oUpdate = { 20, 2 }; Object[] iCols = { 200, 20, "i200" }; TestRow o1Row = new TestRow(oType, o1Cols); TestRow cRow = new TestRow(cType, cCols); TestRow oOrigRow = new TestRow(oType, oOrig); TestRow oUpdateRow = new TestRow(oType, oUpdate); TestRow iRow = new TestRow(iType, iCols); writeRow(tids.o, o1Cols); writeRow(tids.c, cCols); writeRow(tids.o, oOrig); writeRow(tids.i, iCols); compareRows( new Row[] { iRow, oOrigRow, o1Row, cRow }, adapter.newGroupCursor(cType.table().getGroup()) ); // updated o moves after o1 and adopts i updateRow(oOrigRow, oUpdateRow); compareRows( new Row[] { o1Row, cRow, oUpdateRow, iRow }, adapter.newGroupCursor(cType.table().getGroup()) ); } protected void compareRows(Row[] expected, Cursor cursor) { txnService().beginTransaction(session()); try { super.compareRows(expected, cursor); txnService().commitTransaction(session()); } finally { txnService().rollbackTransactionIfOpen(session()); } } }