/**
* 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.keyupdate;
import com.foundationdb.ais.model.Index;
import com.foundationdb.qp.row.Row;
import com.foundationdb.server.api.dml.scan.NewRow;
import org.junit.Test;
public final class GroupIndexLjUpdateIT extends GIUpdateITBase {
@Test
public void placeholderNoOrphan() {
groupIndex("c.name", "o.when");
writeAndCheck(
row(c, 1L, "Bergy"),
"Bergy, null, 1, null => " + containing(c)
);
writeAndCheck(
row(o, 10L, 1L, "01-01-2001"),
"Bergy, 01-01-2001, 1, 10 => " + containing(c, o)
);
}
@Test
public void placeholderWithOrphan() {
groupIndex("c.name", "o.when");
writeAndCheck(
row(o, 10L, 1L, "01-01-2001")
);
writeAndCheck(
row(c, 1L, "Bergy"),
"Bergy, 01-01-2001, 1, 10 => " + containing(c, o)
);
}
@Test
public void deleteSecondOinCO() {
groupIndex("c.name", "o.when");
final Row customer, firstOrder, secondOrder;
writeAndCheck(
customer = row(c, 1L, "Joe"),
"Joe, null, 1, null => " + containing(c)
);
writeAndCheck(
firstOrder = row(o, 11L, 1L, "01-01-01"),
"Joe, 01-01-01, 1, 11 => " + containing(c, o)
);
writeAndCheck(
secondOrder = row(o, 12L, 1L, "02-02-02"),
"Joe, 01-01-01, 1, 11 => " + containing(c, o),
"Joe, 02-02-02, 1, 12 => " + containing(c, o)
);
deleteAndCheck(
secondOrder,
"Joe, 01-01-01, 1, 11 => " + containing(c, o)
);
deleteAndCheck(
firstOrder,
"Joe, null, 1, null => " + containing(c)
);
deleteAndCheck(
customer
);
}
@Test
public void coiGIsNoOrphan() {
groupIndex("c.name", "o.when", "i.sku");
// write write write
writeAndCheck(
row(c, 1L, "Horton"),
"Horton, null, null, 1, null, null => " + containing(c)
);
writeAndCheck(
row(o, 11L, 1L, "01-01-2001"),
"Horton, 01-01-2001, null, 1, 11, null => " + containing(c, o)
);
writeAndCheck(
row(i, 101L, 11L, 1111),
"Horton, 01-01-2001, 1111, 1, 11, 101 => " + containing(c, o, i)
);
writeAndCheck(
row(i, 102L, 11L, 2222),
"Horton, 01-01-2001, 1111, 1, 11, 101 => " + containing(c, o, i),
"Horton, 01-01-2001, 2222, 1, 11, 102 => " + containing(c, o, i)
);
writeAndCheck(
row(i, 103L, 11L, 3333),
"Horton, 01-01-2001, 1111, 1, 11, 101 => " + containing(c, o, i),
"Horton, 01-01-2001, 2222, 1, 11, 102 => " + containing(c, o, i),
"Horton, 01-01-2001, 3333, 1, 11, 103 => " + containing(c, o, i)
);
writeAndCheck(
row(o, 12L, 1L, "02-02-2002"),
"Horton, 01-01-2001, 1111, 1, 11, 101 => " + containing(c, o, i),
"Horton, 01-01-2001, 2222, 1, 11, 102 => " + containing(c, o, i),
"Horton, 01-01-2001, 3333, 1, 11, 103 => " + containing(c, o, i),
"Horton, 02-02-2002, null, 1, 12, null => " + containing(c, o)
);
writeAndCheck(row(a, 10001L, 1L, "Causeway"),
"Horton, 01-01-2001, 1111, 1, 11, 101 => " + containing(c, o, i),
"Horton, 01-01-2001, 2222, 1, 11, 102 => " + containing(c, o, i),
"Horton, 01-01-2001, 3333, 1, 11, 103 => " + containing(c, o, i),
"Horton, 02-02-2002, null, 1, 12, null => " + containing(c, o)
);
// update parent
updateAndCheck(
row(o, 11L, 1L, "01-01-2001"),
row(o, 11L, 1L, "01-01-1999"), // party!
"Horton, 01-01-1999, 1111, 1, 11, 101 => " + containing(c, o, i),
"Horton, 01-01-1999, 2222, 1, 11, 102 => " + containing(c, o, i),
"Horton, 01-01-1999, 3333, 1, 11, 103 => " + containing(c, o, i),
"Horton, 02-02-2002, null, 1, 12, null => " + containing(c, o)
);
// update child
updateAndCheck(
row(i, 102L, 11L, 2222),
row(i, 102L, 11L, 2442),
"Horton, 01-01-1999, 1111, 1, 11, 101 => " + containing(c, o, i),
"Horton, 01-01-1999, 2442, 1, 11, 102 => " + containing(c, o, i),
"Horton, 01-01-1999, 3333, 1, 11, 103 => " + containing(c, o, i),
"Horton, 02-02-2002, null, 1, 12, null => " + containing(c, o)
);
// delete child
deleteAndCheck(
row(i, 102L, 11L, 222211),
"Horton, 01-01-1999, 1111, 1, 11, 101 => " + containing(c, o, i),
"Horton, 01-01-1999, 3333, 1, 11, 103 => " + containing(c, o, i),
"Horton, 02-02-2002, null, 1, 12, null => " + containing(c, o)
);
// delete parent
deleteAndCheck(
row(o, 11L, 1L, "01-01-2001"),
"Horton, 02-02-2002, null, 1, 12, null => " + containing(c, o)
);
}
@Test
public void createGIOnFullyPopulatedTables() {
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001"),
row(i, 101L, 11L, 1111)
);
groupIndexNamed("name_when_sku", "c.name", "o.when", "i.sku");
checkIndex("name_when_sku",
"Horton, 01-01-2001, 1111, 1, 11, 101 => " + containing("name_when_sku", c, o, i)
);
}
@Test
public void createGIOnPartiallyPopulatedTablesFromRoot() {
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001")
);
groupIndexNamed("name_when_sku", "c.name", "o.when", "i.sku");
checkIndex("name_when_sku",
"Horton, 01-01-2001, null, 1, 11, null => " + containing("name_when_sku", c, o)
);
}
@Test
public void createGIOnPartiallyPopulatedTablesFromMiddle() {
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001")
);
groupIndexNamed("when_sku", "o.when", "i.sku");
checkIndex("when_sku",
"01-01-2001, null, 1, 11, null => " + containing("when_sku", o)
);
}
@Test
public void ihIndexNoOrphans() {
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001"),
row(i, 101L, 11L, 1111),
row(h, 1001L, 101L, "handle with care")
);
checkIndex(indexName, "1111, handle with care, 1, 11, 101, 1001 => " + containing(i, h));
// delete from root on down
deleteRow(c, 1L, "Horton");
checkIndex(indexName, "1111, handle with care, 1, 11, 101, 1001 => " + containing(i, h));
deleteRow(o, 11L, 1L, "01-01-2001 => " + containing(i, h));
checkIndex(indexName, "1111, handle with care, null, 11, 101, 1001 => " + containing(i, h));
deleteRow(i, 101L, 11L, 1111);
checkIndex(indexName);
deleteRow(h, 1001L, 101L, "handle with care");
checkIndex(indexName);
}
@Test
public void ihIndexOIsOrphaned() {
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(o, 11L, 1L, "01-01-2001"),
row(i, 101L, 11L, 1111),
row(h, 1001L, 101L, "handle with care")
);
checkIndex(indexName, "1111, handle with care, 1, 11, 101, 1001 => " + containing(i, h));
// delete from root on down
deleteRow(o, 11L, 1L, "01-01-2001");
checkIndex(indexName, "1111, handle with care, null, 11, 101, 1001 => " + containing(i, h));
deleteRow(i, 101L, 11L, 1111);
checkIndex(indexName);
deleteRow(h, 1001L, 101L, "handle with care");
checkIndex(indexName);
}
@Test
public void ihIndexIIsOrphaned() {
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(c, -1L, "Notroh"),
row(i, 101L, 11L, 1111),
row(h, 1001L, 101L, "handle with care")
);
checkIndex(indexName, "1111, handle with care, null, 11, 101, 1001 => " + containing(i, h));
// delete from root on up
deleteRow(i, 101L, 11L, 1111);
checkIndex(indexName);
deleteRow(h, 1001L, 101L, "handle with care");
checkIndex(indexName);
}
@Test
public void ihIndexIIsOrphanedButCExists() {
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(i, 101L, 11L, 1111),
row(h, 1001L, 101L, "handle with care")
);
checkIndex(indexName, "1111, handle with care, null, 11, 101, 1001 => " + containing(i, h));
// delete from root on up
deleteRow(i, 101L, 11L, 1111);
checkIndex(indexName);
deleteRow(h, 1001L, 101L, "handle with care");
checkIndex(indexName);
}
@Test
public void ihIndexHIsOrphaned() {
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(h, 1001L, 101L, "handle with care")
);
checkIndex(indexName);
// delete from root on up
deleteRow(h, 1001L, 101L, "handle with care");
checkIndex(indexName);
}
@Test
public void adoptionChangesHKeyNoCustomer() {
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(i, 101L, 11L, 1111),
row(h, 1001L, 101L, "handle with care")
);
checkIndex(indexName,
"1111, handle with care, null, 11, 101, 1001 => " + containing(i, h)
);
// bring an o that adopts the i
writeRow(o, 11L, 1L, "01-01-2001");
checkIndex(indexName,
"1111, handle with care, 1, 11, 101, 1001 => " + containing(i, h)
);
}
@Test
public void adoptionChangesHKeyWithC() {
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(i, 101L, 11L, 1111),
row(h, 1001L, 101L, "handle with care")
);
checkIndex(indexName,
"1111, handle with care, null, 11, 101, 1001 => " + containing(i, h)
);
// bring an o that adopts the i
writeRow(o, 11L, 1L, "01-01-2001");
checkIndex(indexName,
"1111, handle with care, 1, 11, 101, 1001 => " + containing(i, h)
);
}
@Test
public void testTwoBranches() {
groupIndexNamed("when_name", "o.when", "c.name");
groupIndexNamed("second_idx", "c.name", "a.street");
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001"),
row(o, 12L, 1L, "03-03-2003"),
row(a, 21L, 1L, "Harrington"),
row(a, 22L, 1L, "Causeway"),
row(c, 2L, "David"),
row(o, 13L, 2L, "02-02-2002"),
row(a, 23L, 2L, "Highland")
);
checkIndex(
"when_name",
"01-01-2001, Horton, 1, 11 => " + containing("when_name", c, o),
"02-02-2002, David, 2, 13 => " + containing("when_name", c, o),
"03-03-2003, Horton, 1, 12 => " + containing("when_name", c, o)
);
checkIndex(
"second_idx",
"David, Highland, 2, 23 => " + containing("second_idx", c, a),
"Horton, Causeway, 1, 22 => " + containing("second_idx", c, a),
"Horton, Harrington, 1, 21 => " + containing("second_idx",c, a)
);
}
@Test
public void updateModifiesHKeyWithinBranch() {
// branch is I-H, we're modifying the hkey of an H
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(row(c, 1L, "Horton"));
checkIndex(indexName);
writeRows(row(o, 11L, 1L, "01-01-2001"));
checkIndex(indexName);
writeRows(row(i, 101L, 11L, "1111"));
checkIndex(indexName, "1111, null, 1, 11, 101, null => " + containing(i));
writeRows(row(h, 1001L, 101L, "don't break"));
checkIndex(indexName, "1111, don't break, 1, 11, 101, 1001 => " + containing(i, h));
writeRows(row(c, 2L, "David"));
checkIndex(indexName, "1111, don't break, 1, 11, 101, 1001 => " + containing(i, h));
writeRows(row(o, 12L, 2L, "02-02-2002"));
checkIndex(indexName, "1111, don't break, 1, 11, 101, 1001 => " + containing(i, h));
writeRows(row(i, 102L, 12L, "2222"));
checkIndex(
indexName,
"1111, don't break, 1, 11, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
updateRow(
row(h, 1001L, 101L, "don't break"),
row(h, 1001L, 102L, "don't break")
);
checkIndex(
indexName,
"1111, null, 1, 11, 101, null => " + containing(i),
"2222, don't break, 2, 12, 102, 1001 => " + containing(i, h)
);
}
@Test
public void updateModifiesHKeyDirectlyAboveBranch() {
// branch is I-H, we're modifying the hkey of an I
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001"),
row(i, 101L, 11L, "1111"),
row(h, 1001L, 101L, "don't break"),
row(c, 2L, "David"),
row(o, 12L, 2L, "02-02-2002"),
row(i, 102L, 12L, "2222")
);
checkIndex(
indexName,
"1111, don't break, 1, 11, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
updateRow(
row(i, 101L, 11L, "1111"),
row(i, 101L, 12L, "1111")
);
// TODO: This test fails on the first row. last two nulls are 101, 1001, reflecting the old home
// TODO: of the row. Ancestor lookup broken due to use of wrong column?
checkIndex(
indexName,
"1111, don't break, 2, 12, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
}
@Test
public void updateModifiesHKeyHigherAboveBranch() {
// branch is I-H, we're modifying the hkey of an O referenced by an I
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001"),
row(i, 101L, 11L, "1111"),
row(h, 1001L, 101L, "don't break"),
row(c, 2L, "David"),
row(o, 12L, 2L, "02-02-2002"),
row(i, 102L, 12L, "2222")
);
checkIndex(
indexName,
"1111, don't break, 1, 11, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
updateRow(
row(o, 11L, 1L, "01-01-2001"),
row(o, 11L, 2L, "01-01-2001")
);
checkIndex(
indexName,
"1111, don't break, 2, 11, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
}
@Test
public void updateOrphansHKeyWithinBranch() {
// branch is I-H, we're modifying the hkey of an H
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001"),
row(i, 101L, 11L, "1111"),
row(h, 1001L, 101L, "don't break"),
row(c, 2L, "David"),
row(o, 12L, 2L, "02-02-2002"),
row(i, 102L, 12L, "2222")
);
checkIndex(
indexName,
"1111, don't break, 1, 11, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
updateRow(
row(h, 1001L, 101L, "don't break"),
row(h, 1001L, 666L, "don't break")
);
checkIndex(indexName,
"1111, null, 1, 11, 101, null => " + containing(i),
"2222, null, 2, 12, 102, null => " + containing(i)
);
}
@Test
public void updateMovesHKeyWithinBranch() {
// branch is I-H, we're modifying the hkey of an H
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001"),
row(i, 101L, 11L, "1111"),
row(h, 1001L, 101L, "don't break"),
row(c, 2L, "David"),
row(o, 12L, 2L, "02-02-2002"),
row(i, 102L, 12L, "2222"),
row(o, 66L, 6L, "03-03-2003"),
row(i, 666L, 66L, "6666")
);
checkIndex(
indexName,
"1111, don't break, 1, 11, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i),
"6666, null, 6, 66, 666, null => " + containing(i)
);
updateRow(
row(h, 1001L, 101L, "don't break"),
row(h, 1001L, 666L, "don't break")
);
checkIndex(
indexName,
"1111, null, 1, 11, 101, null => " + containing(i),
"2222, null, 2, 12, 102, null => " + containing(i),
"6666, don't break, 6, 66, 666, 1001 => " + containing(i, h)
);
}
@Test
public void updateOrphansHKeyDirectlyAboveBranch() {
// branch is I-H, we're modifying the hkey of an I
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001"),
row(i, 101L, 11L, "1111"),
row(h, 1001L, 101L, "don't break"),
row(c, 2L, "David"),
row(o, 12L, 2L, "02-02-2002"),
row(i, 102L, 12L, "2222")
);
checkIndex(
indexName,
"1111, don't break, 1, 11, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
updateRow(
row(i, 101L, 11L, "1111"),
row(i, 101L, 66L, "1111")
);
checkIndex(
indexName,
"1111, don't break, null, 66, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
}
@Test
public void updateOrphansHKeyHigherAboveBranch() {
// branch is I-H, we're modifying the hkey of an O referenced by an I
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 1L, "01-01-2001"),
row(i, 101L, 11L, "1111"),
row(h, 1001L, 101L, "don't break"),
row(c, 2L, "David"),
row(o, 12L, 2L, "02-02-2002"),
row(i, 102L, 12L, "2222")
);
checkIndex(
indexName,
"1111, don't break, 1, 11, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
updateRow(
row(o, 11L, 1L, "01-01-2001"),
row(o, 11L, 6L, "01-01-2001")
);
checkIndex(
indexName,
"1111, don't break, 6, 11, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
}
/**
* Create the endgame of {@linkplain #updateOrphansHKeyHigherAboveBranch} initially, as a santy check
*/
@Test
public void originallyOrphansHKeyHigherAboveBranch() {
String indexName = groupIndex("i.sku", "h.handling_instructions");
writeRows(
row(c, 1L, "Horton"),
row(o, 11L, 6L, "01-01-2001"),
row(i, 101L, 11L, "1111"),
row(h, 1001L, 101L, "don't break"),
row(c, 2L, "David"),
row(o, 12L, 2L, "02-02-2002"),
row(i, 102L, 12L, "2222")
);
checkIndex(
indexName,
"1111, don't break, 6, 11, 101, 1001 => " + containing(i, h),
"2222, null, 2, 12, 102, null => " + containing(i)
);
}
public GroupIndexLjUpdateIT() {
super(Index.JoinType.LEFT);
}
}