/**
* 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.mt;
import com.foundationdb.qp.rowtype.RowType;
import com.foundationdb.qp.util.SchemaCache;
import com.foundationdb.server.error.DuplicateKeyException;
import com.foundationdb.server.test.mt.util.ConcurrentTestBuilder;
import com.foundationdb.server.test.mt.util.ConcurrentTestBuilderImpl;
import com.foundationdb.server.test.mt.util.MonitoredThread;
import com.foundationdb.server.test.mt.util.ThreadHelper;
import com.foundationdb.server.test.mt.util.ThreadHelper.UncaughtHandler;
import com.foundationdb.server.test.mt.util.ThreadMonitor.Stage;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
public class ConcurrentUniqueInsertMT extends MTBase
{
private int tid;
private RowType rowType;
private void createNotNullUnique() {
tid = createTable("test", "t", "id INT NOT NULL PRIMARY KEY, x INT NOT NULL, UNIQUE(x)");
rowType = SchemaCache.globalSchema(ais()).tableRowType(tid);
}
private void createNullUnique() {
tid = createTable("test", "t", "id INT NOT NULL PRIMARY KEY, x INT NULL, UNIQUE(x)");
rowType = SchemaCache.globalSchema(ais()).tableRowType(tid);
}
private List<MonitoredThread> buildThreads(Integer x1, Integer x2) {
ConcurrentTestBuilder builder = ConcurrentTestBuilderImpl.create();
builder.add("insert_a", insertCreator(tid, testRow(rowType, 1, x1)))
.sync("sync", Stage.POST_BEGIN)
.add("insert_b", insertCreator(tid, testRow(rowType, 2, x2)))
.sync("sync", Stage.POST_BEGIN);
return builder.build(this);
}
private void expectSuccess(List<MonitoredThread> threads) {
ThreadHelper.runAndCheck(threads);
}
private void expectOneFailure(List<MonitoredThread> threads) {
UncaughtHandler handler = ThreadHelper.startAndJoin(threads);
assertEquals("failure count", 1, handler.thrown.size());
Throwable failure = handler.thrown.values().iterator().next();
assertEquals("failure was dup", DuplicateKeyException.class, failure.getClass());
assertEquals("saw rollback", true, threads.get(0).hadRollback() || threads.get(1).hadRollback());
}
@Test
public void notNullSameValue() {
createNotNullUnique();
expectOneFailure(buildThreads(42, 42));
}
@Test
public void notNullDifferentValue() {
createNotNullUnique();
expectSuccess(buildThreads(42, 52));
}
@Test
public void nullBothNull() {
createNullUnique();
expectSuccess(buildThreads(null, null));
}
@Test
public void nullSameValue() {
createNotNullUnique();
expectOneFailure(buildThreads(42, 42));
}
@Test
public void nullDifferentValue() {
createNotNullUnique();
expectSuccess(buildThreads(42, 52));
}
}