/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.bench;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
/**
* This test is similar to the TPC-C test of the Transaction Processing Council
* (TPC). Only one connection and one thread is used. Referential integrity is
* not implemented.
* <p>
* See also http://www.tpc.org
*/
public class BenchC implements Bench {
private static final int COMMIT_EVERY = 1000;
private static final String[] TABLES = { "WAREHOUSE", "DISTRICT",
"CUSTOMER", "HISTORY", "ORDERS", "NEW_ORDER", "ITEM", "STOCK",
"ORDER_LINE", "RESULTS" };
private static final String[] CREATE_SQL = {
"CREATE TABLE WAREHOUSE(\n" +
" W_ID INT NOT NULL PRIMARY KEY,\n" +
" W_NAME VARCHAR(10),\n" +
" W_STREET_1 VARCHAR(20),\n" +
" W_STREET_2 VARCHAR(20),\n" +
" W_CITY VARCHAR(20),\n" +
" W_STATE CHAR(2),\n" +
" W_ZIP CHAR(9),\n" +
" W_TAX DECIMAL(4, 4),\n" +
" W_YTD DECIMAL(12, 2))",
"CREATE TABLE DISTRICT(\n" +
" D_ID INT NOT NULL,\n" +
" D_W_ID INT NOT NULL,\n" +
" D_NAME VARCHAR(10),\n" +
" D_STREET_1 VARCHAR(20),\n" +
" D_STREET_2 VARCHAR(20),\n" +
" D_CITY VARCHAR(20),\n" +
" D_STATE CHAR(2),\n" +
" D_ZIP CHAR(9),\n" +
" D_TAX DECIMAL(4, 4),\n" +
" D_YTD DECIMAL(12, 2),\n" +
" D_NEXT_O_ID INT,\n" +
" PRIMARY KEY (D_ID, D_W_ID))",
// + " FOREIGN KEY (D_W_ID)\n"
// + " REFERENCES WAREHOUSE(W_ID))",
"CREATE TABLE CUSTOMER(\n" +
" C_ID INT NOT NULL,\n" +
" C_D_ID INT NOT NULL,\n" +
" C_W_ID INT NOT NULL,\n" +
" C_FIRST VARCHAR(16),\n" +
" C_MIDDLE CHAR(2),\n" +
" C_LAST VARCHAR(16),\n" +
" C_STREET_1 VARCHAR(20),\n" +
" C_STREET_2 VARCHAR(20),\n" +
" C_CITY VARCHAR(20),\n" +
" C_STATE CHAR(2),\n" +
" C_ZIP CHAR(9),\n" +
" C_PHONE CHAR(16),\n" +
" C_SINCE TIMESTAMP,\n" +
" C_CREDIT CHAR(2),\n" +
" C_CREDIT_LIM DECIMAL(12, 2),\n" +
" C_DISCOUNT DECIMAL(4, 4),\n" +
" C_BALANCE DECIMAL(12, 2),\n" +
" C_YTD_PAYMENT DECIMAL(12, 2),\n" +
" C_PAYMENT_CNT DECIMAL(4),\n" +
" C_DELIVERY_CNT DECIMAL(4),\n" +
" C_DATA VARCHAR(500),\n" +
" PRIMARY KEY (C_W_ID, C_D_ID, C_ID))",
// + " FOREIGN KEY (C_W_ID, C_D_ID)\n"
// + " REFERENCES DISTRICT(D_W_ID, D_ID))",
"CREATE INDEX CUSTOMER_NAME ON CUSTOMER(C_LAST, C_D_ID, C_W_ID)",
"CREATE TABLE HISTORY(\n" +
" H_C_ID INT,\n" +
" H_C_D_ID INT,\n" +
" H_C_W_ID INT,\n" +
" H_D_ID INT,\n" +
" H_W_ID INT,\n" +
" H_DATE TIMESTAMP,\n" +
" H_AMOUNT DECIMAL(6, 2),\n" +
" H_DATA VARCHAR(24))",
// + " FOREIGN KEY(H_C_W_ID, H_C_D_ID, H_C_ID)\n"
// + " REFERENCES CUSTOMER(C_W_ID, C_D_ID, C_ID),\n"
// + " FOREIGN KEY(H_W_ID, H_D_ID)\n"
// + " REFERENCES DISTRICT(D_W_ID, D_ID))",
"CREATE TABLE ORDERS(\n" +
" O_ID INT NOT NULL,\n" +
" O_D_ID INT NOT NULL,\n" +
" O_W_ID INT NOT NULL,\n" +
" O_C_ID INT,\n" +
" O_ENTRY_D TIMESTAMP,\n" +
" O_CARRIER_ID INT,\n" +
" O_OL_CNT INT,\n" +
" O_ALL_LOCAL DECIMAL(1),\n" +
" PRIMARY KEY(O_W_ID, O_D_ID, O_ID))",
// + " FOREIGN KEY(O_W_ID, O_D_ID, O_C_ID)\n"
// + " REFERENCES CUSTOMER(C_W_ID, C_D_ID, C_ID))",
"CREATE INDEX ORDERS_OID ON ORDERS(O_ID)",
"CREATE TABLE NEW_ORDER(\n" +
" NO_O_ID INT NOT NULL,\n" +
" NO_D_ID INT NOT NULL,\n" +
" NO_W_ID INT NOT NULL,\n" +
" PRIMARY KEY(NO_W_ID, NO_D_ID, NO_O_ID))",
// + " FOREIGN KEY(NO_W_ID, NO_D_ID, NO_O_ID)\n"
// + " REFERENCES ORDER(O_W_ID, O_D_ID, O_ID))",
"CREATE TABLE ITEM(\n" +
" I_ID INT NOT NULL,\n" +
" I_IM_ID INT,\n" +
" I_NAME VARCHAR(24),\n" +
" I_PRICE DECIMAL(5, 2),\n" +
" I_DATA VARCHAR(50),\n" +
" PRIMARY KEY(I_ID))",
"CREATE TABLE STOCK(\n" +
" S_I_ID INT NOT NULL,\n" +
" S_W_ID INT NOT NULL,\n" +
" S_QUANTITY DECIMAL(4),\n" +
" S_DIST_01 CHAR(24),\n" +
" S_DIST_02 CHAR(24),\n" +
" S_DIST_03 CHAR(24),\n" +
" S_DIST_04 CHAR(24),\n" +
" S_DIST_05 CHAR(24),\n" +
" S_DIST_06 CHAR(24),\n" +
" S_DIST_07 CHAR(24),\n" +
" S_DIST_08 CHAR(24),\n" +
" S_DIST_09 CHAR(24),\n" +
" S_DIST_10 CHAR(24),\n" +
" S_YTD DECIMAL(8),\n" +
" S_ORDER_CNT DECIMAL(4),\n" +
" S_REMOTE_CNT DECIMAL(4),\n" +
" S_DATA VARCHAR(50),\n" +
" PRIMARY KEY(S_W_ID, S_I_ID))",
// + " FOREIGN KEY(S_W_ID)\n"
// + " REFERENCES WAREHOUSE(W_ID),\n"
// + " FOREIGN KEY(S_I_ID)\n" + " REFERENCES ITEM(I_ID))",
"CREATE TABLE ORDER_LINE(\n" +
" OL_O_ID INT NOT NULL,\n" +
" OL_D_ID INT NOT NULL,\n" +
" OL_W_ID INT NOT NULL,\n" +
" OL_NUMBER INT NOT NULL,\n" +
" OL_I_ID INT,\n" +
" OL_SUPPLY_W_ID INT,\n" +
" OL_DELIVERY_D TIMESTAMP,\n" +
" OL_QUANTITY DECIMAL(2),\n" +
" OL_AMOUNT DECIMAL(6, 2),\n" +
" OL_DIST_INFO CHAR(24),\n" +
" PRIMARY KEY (OL_W_ID, OL_D_ID, OL_O_ID, OL_NUMBER))",
// + " FOREIGN KEY(OL_W_ID, OL_D_ID, OL_O_ID)\n"
// + " REFERENCES ORDER(O_W_ID, O_D_ID, O_ID),\n"
// + " FOREIGN KEY(OL_SUPPLY_W_ID, OL_I_ID)\n"
// + " REFERENCES STOCK(S_W_ID, S_I_ID))",
"CREATE TABLE RESULTS(\n" +
" ID INT NOT NULL PRIMARY KEY,\n" +
" TERMINAL INT,\n" +
" OPERATION INT,\n" +
" RESPONSE_TIME INT,\n" +
" PROCESSING_TIME INT,\n" +
" KEYING_TIME INT,\n" +
" THINK_TIME INT,\n" +
" SUCCESSFUL INT,\n" +
" NOW TIMESTAMP)" };
int warehouses = 2;
int items = 10000;
int districtsPerWarehouse = 10;
int customersPerDistrict = 300;
private Database database;
private int ordersPerDistrict = 300;
private BenchCRandom random;
private String action;
@Override
public void init(Database db, int size) throws SQLException {
this.database = db;
random = new BenchCRandom();
items = size * 10;
warehouses = 2;
districtsPerWarehouse = Math.max(1, size / 100);
customersPerDistrict = Math.max(1, size / 100);
ordersPerDistrict = Math.max(1, size / 1000);
db.start(this, "Init");
db.openConnection();
load();
db.commit();
db.closeConnection();
db.end();
// db.start(this, "Open/Close");
// db.openConnection();
// db.closeConnection();
// db.end();
}
private void load() throws SQLException {
for (String sql : TABLES) {
database.dropTable(sql);
}
for (String sql : CREATE_SQL) {
database.update(sql);
}
database.setAutoCommit(false);
loadItem();
loadWarehouse();
loadCustomer();
loadOrder();
database.commit();
trace("Load done");
}
private void trace(String s) {
action = s;
}
private void trace(int i, int max) {
database.trace(action, i, max);
}
private void loadItem() throws SQLException {
trace("Loading item table");
boolean[] original = random.getBoolean(items, items / 10);
PreparedStatement prep = database.prepare(
"INSERT INTO ITEM(I_ID, I_IM_ID, I_NAME, I_PRICE, I_DATA) " +
"VALUES(?, ?, ?, ?, ?)");
for (int id = 1; id <= items; id++) {
String name = random.getString(14, 24);
BigDecimal price = random.getBigDecimal(random.getInt(100, 10000), 2);
String data = random.getString(26, 50);
if (original[id - 1]) {
data = random.replace(data, "original");
}
prep.setInt(1, id);
prep.setInt(2, random.getInt(1, 10000));
prep.setString(3, name);
prep.setBigDecimal(4, price);
prep.setString(5, data);
database.update(prep, "insertItem");
trace(id, items);
if (id % COMMIT_EVERY == 0) {
database.commit();
}
}
}
private void loadWarehouse() throws SQLException {
trace("Loading warehouse table");
PreparedStatement prep = database.prepare(
"INSERT INTO WAREHOUSE(W_ID, W_NAME, W_STREET_1, " +
"W_STREET_2, W_CITY, W_STATE, W_ZIP, W_TAX, W_YTD) " +
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)");
for (int id = 1; id <= warehouses; id++) {
String name = random.getString(6, 10);
String[] address = random.getAddress();
String street1 = address[0];
String street2 = address[1];
String city = address[2];
String state = address[3];
String zip = address[4];
BigDecimal tax = random.getBigDecimal(random.getInt(0, 2000), 4);
BigDecimal ytd = new BigDecimal("300000.00");
prep.setInt(1, id);
prep.setString(2, name);
prep.setString(3, street1);
prep.setString(4, street2);
prep.setString(5, city);
prep.setString(6, state);
prep.setString(7, zip);
prep.setBigDecimal(8, tax);
prep.setBigDecimal(9, ytd);
database.update(prep, "insertWarehouse");
loadStock(id);
loadDistrict(id);
if (id % COMMIT_EVERY == 0) {
database.commit();
}
}
}
private void loadCustomer() throws SQLException {
trace("Load customer table");
int max = warehouses * districtsPerWarehouse;
int i = 0;
for (int id = 1; id <= warehouses; id++) {
for (int districtId = 1; districtId <= districtsPerWarehouse; districtId++) {
loadCustomerSub(districtId, id);
trace(i++, max);
if (i % COMMIT_EVERY == 0) {
database.commit();
}
}
}
}
private void loadCustomerSub(int dId, int wId) throws SQLException {
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
PreparedStatement prepCustomer = database.prepare(
"INSERT INTO CUSTOMER(C_ID, C_D_ID, C_W_ID, " +
"C_FIRST, C_MIDDLE, C_LAST, " +
"C_STREET_1, C_STREET_2, C_CITY, C_STATE, C_ZIP, " +
"C_PHONE, C_SINCE, C_CREDIT, " +
"C_CREDIT_LIM, C_DISCOUNT, C_BALANCE, C_DATA, " +
"C_YTD_PAYMENT, C_PAYMENT_CNT, C_DELIVERY_CNT) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
PreparedStatement prepHistory = database.prepare(
"INSERT INTO HISTORY(H_C_ID, H_C_D_ID, H_C_W_ID, " +
"H_W_ID, H_D_ID, H_DATE, H_AMOUNT, H_DATA) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
for (int cId = 1; cId <= customersPerDistrict; cId++) {
String first = random.getString(8, 16);
String middle = "OE";
String last;
if (cId < 1000) {
last = random.getLastname(cId);
} else {
last = random.getLastname(random.getNonUniform(255, 0, 999));
}
String[] address = random.getAddress();
String street1 = address[0];
String street2 = address[1];
String city = address[2];
String state = address[3];
String zip = address[4];
String phone = random.getNumberString(16, 16);
String credit;
if (random.getInt(0, 1) == 0) {
credit = "GC";
} else {
credit = "BC";
}
BigDecimal discount = random.getBigDecimal(random.getInt(0, 5000), 4);
BigDecimal balance = new BigDecimal("-10.00");
BigDecimal creditLim = new BigDecimal("50000.00");
String data = random.getString(300, 500);
BigDecimal ytdPayment = new BigDecimal("10.00");
int paymentCnt = 1;
int deliveryCnt = 1;
prepCustomer.setInt(1, cId);
prepCustomer.setInt(2, dId);
prepCustomer.setInt(3, wId);
prepCustomer.setString(4, first);
prepCustomer.setString(5, middle);
prepCustomer.setString(6, last);
prepCustomer.setString(7, street1);
prepCustomer.setString(8, street2);
prepCustomer.setString(9, city);
prepCustomer.setString(10, state);
prepCustomer.setString(11, zip);
prepCustomer.setString(12, phone);
prepCustomer.setTimestamp(13, timestamp);
prepCustomer.setString(14, credit);
prepCustomer.setBigDecimal(15, creditLim);
prepCustomer.setBigDecimal(16, discount);
prepCustomer.setBigDecimal(17, balance);
prepCustomer.setString(18, data);
prepCustomer.setBigDecimal(19, ytdPayment);
prepCustomer.setInt(20, paymentCnt);
prepCustomer.setInt(21, deliveryCnt);
database.update(prepCustomer, "insertCustomer");
BigDecimal amount = new BigDecimal("10.00");
String hData = random.getString(12, 24);
prepHistory.setInt(1, cId);
prepHistory.setInt(2, dId);
prepHistory.setInt(3, wId);
prepHistory.setInt(4, wId);
prepHistory.setInt(5, dId);
prepHistory.setTimestamp(6, timestamp);
prepHistory.setBigDecimal(7, amount);
prepHistory.setString(8, hData);
database.update(prepHistory, "insertHistory");
}
}
private void loadOrder() throws SQLException {
trace("Loading order table");
int max = warehouses * districtsPerWarehouse;
int i = 0;
for (int wId = 1; wId <= warehouses; wId++) {
for (int dId = 1; dId <= districtsPerWarehouse; dId++) {
loadOrderSub(dId, wId);
trace(i++, max);
}
}
}
private void loadOrderSub(int dId, int wId) throws SQLException {
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
int[] orderid = random.getPermutation(ordersPerDistrict);
PreparedStatement prepOrder = database.prepare(
"INSERT INTO ORDERS(O_ID, O_C_ID, O_D_ID, O_W_ID, " +
"O_ENTRY_D, O_CARRIER_ID, O_OL_CNT, O_ALL_LOCAL) " +
"VALUES(?, ?, ?, ?, ?, ?, ?, 1)");
PreparedStatement prepNewOrder = database.prepare(
"INSERT INTO NEW_ORDER (NO_O_ID, NO_D_ID, NO_W_ID) " +
"VALUES (?, ?, ?)");
PreparedStatement prepLine = database.prepare(
"INSERT INTO ORDER_LINE(" +
"OL_O_ID, OL_D_ID, OL_W_ID, OL_NUMBER, " +
"OL_I_ID, OL_SUPPLY_W_ID, OL_QUANTITY, OL_AMOUNT, " +
"OL_DIST_INFO, OL_DELIVERY_D)" +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)");
for (int oId = 1, i = 0; oId <= ordersPerDistrict; oId++) {
int cId = orderid[oId - 1];
int carrierId = random.getInt(1, 10);
int olCnt = random.getInt(5, 15);
prepOrder.setInt(1, oId);
prepOrder.setInt(2, cId);
prepOrder.setInt(3, dId);
prepOrder.setInt(4, wId);
prepOrder.setTimestamp(5, timestamp);
prepOrder.setInt(7, olCnt);
if (oId <= 2100) {
prepOrder.setInt(6, carrierId);
} else {
// the last 900 orders have not been delivered
prepOrder.setNull(6, Types.INTEGER);
prepNewOrder.setInt(1, oId);
prepNewOrder.setInt(2, dId);
prepNewOrder.setInt(3, wId);
database.update(prepNewOrder, "newNewOrder");
}
database.update(prepOrder, "insertOrder");
for (int ol = 1; ol <= olCnt; ol++) {
int id = random.getInt(1, items);
int supplyId = wId;
int quantity = 5;
String distInfo = random.getString(24);
BigDecimal amount;
if (oId < 2101) {
amount = random.getBigDecimal(0, 2);
} else {
amount = random.getBigDecimal(random.getInt(0, 1000000), 2);
}
prepLine.setInt(1, oId);
prepLine.setInt(2, dId);
prepLine.setInt(3, wId);
prepLine.setInt(4, ol);
prepLine.setInt(5, id);
prepLine.setInt(6, supplyId);
prepLine.setInt(7, quantity);
prepLine.setBigDecimal(8, amount);
prepLine.setString(9, distInfo);
database.update(prepLine, "insertOrderLine");
if (i++ % COMMIT_EVERY == 0) {
database.commit();
}
}
}
}
private void loadStock(int wId) throws SQLException {
trace("Loading stock table (warehouse " + wId + ")");
boolean[] original = random.getBoolean(items, items / 10);
PreparedStatement prep = database.prepare(
"INSERT INTO STOCK(S_I_ID, S_W_ID, S_QUANTITY, " +
"S_DIST_01, S_DIST_02, S_DIST_03, S_DIST_04, S_DIST_05, " +
"S_DIST_06, S_DIST_07, S_DIST_08, S_DIST_09, S_DIST_10, " +
"S_DATA, S_YTD, S_ORDER_CNT, S_REMOTE_CNT) " +
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
for (int id = 1; id <= items; id++) {
int quantity = random.getInt(10, 100);
String dist01 = random.getString(24);
String dist02 = random.getString(24);
String dist03 = random.getString(24);
String dist04 = random.getString(24);
String dist05 = random.getString(24);
String dist06 = random.getString(24);
String dist07 = random.getString(24);
String dist08 = random.getString(24);
String dist09 = random.getString(24);
String dist10 = random.getString(24);
String data = random.getString(26, 50);
if (original[id - 1]) {
data = random.replace(data, "original");
}
prep.setInt(1, id);
prep.setInt(2, wId);
prep.setInt(3, quantity);
prep.setString(4, dist01);
prep.setString(5, dist02);
prep.setString(6, dist03);
prep.setString(7, dist04);
prep.setString(8, dist05);
prep.setString(9, dist06);
prep.setString(10, dist07);
prep.setString(11, dist08);
prep.setString(12, dist09);
prep.setString(13, dist10);
prep.setString(14, data);
prep.setInt(15, 0);
prep.setInt(16, 0);
prep.setInt(17, 0);
database.update(prep, "insertStock");
if (id % COMMIT_EVERY == 0) {
database.commit();
}
trace(id, items);
}
}
private void loadDistrict(int wId) throws SQLException {
BigDecimal ytd = new BigDecimal("300000.00");
int nextId = 3001;
PreparedStatement prep = database.prepare(
"INSERT INTO DISTRICT(D_ID, D_W_ID, D_NAME, " +
"D_STREET_1, D_STREET_2, D_CITY, D_STATE, D_ZIP, " +
"D_TAX, D_YTD, D_NEXT_O_ID) " +
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
for (int dId = 1; dId <= districtsPerWarehouse; dId++) {
String name = random.getString(6, 10);
String[] address = random.getAddress();
String street1 = address[0];
String street2 = address[1];
String city = address[2];
String state = address[3];
String zip = address[4];
BigDecimal tax = random.getBigDecimal(random.getInt(0, 2000), 4);
prep.setInt(1, dId);
prep.setInt(2, wId);
prep.setString(3, name);
prep.setString(4, street1);
prep.setString(5, street2);
prep.setString(6, city);
prep.setString(7, state);
prep.setString(8, zip);
prep.setBigDecimal(9, tax);
prep.setBigDecimal(10, ytd);
prep.setInt(11, nextId);
database.update(prep, "insertDistrict");
trace(dId, districtsPerWarehouse);
}
}
@Override
public void runTest() throws SQLException {
database.start(this, "Transactions");
database.openConnection();
for (int i = 0; i < 70; i++) {
BenchCThread process = new BenchCThread(database, this, random, i);
process.process();
}
database.closeConnection();
database.end();
database.openConnection();
BenchCThread process = new BenchCThread(database, this, random, 0);
process.process();
database.logMemory(this, "Memory Usage");
database.closeConnection();
}
@Override
public String getName() {
return "BenchC";
}
}