/**
* 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.rowdata;
import java.util.Arrays;
import com.foundationdb.ais.model.AkibanInformationSchema;
import com.foundationdb.server.AkServerUtil;
import junit.framework.TestCase;
public class RowDefTest extends TestCase {
private final static boolean VERBOSE = false;
private final static String[][] TEST_CASES = new String[][] {
{
"create table test(",
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s smallint ", n()),
");"
},
{
"create table test(",
String.format(" %s varchar(100), ", n()),
String.format(" %s int, ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s int, ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s int, ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s int ", n()),
");"
},
{
"create table test(",
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int ", n()),
");"
},
{
"create table test(",
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100), ", n()),
String.format(" %s varchar(100) ", n()),
");"
},
{
"create table test(",
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s smallint, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s bigint, ", n()),
String.format(" %s int, ", n()),
String.format(" %s smallint, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s bigint, ", n()),
String.format(" %s int, ", n()),
String.format(" %s varchar(200), ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s smallint, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s bigint, ", n()),
String.format(" %s int, ", n()),
String.format(" %s smallint, ", n()),
String.format(" %s int, ", n()),
String.format(" %s int, ", n()),
String.format(" %s bigint, ", n()),
String.format(" %s varchar(200), ", n()),
String.format(" %s int, ", n()),
String.format(" %s int ", n()),
");"
}
};
private final static Object[][][] DATA_CASES = new Object[][][] {
new Object[][] { new Object[] { 1, 2, 3 },
new Object[] { null, null, null },
new Object[] { null, 2, null },
new Object[] { 1, null, 3 }, },
new Object[][] {
new Object[] { null, 2, null, 4, null, 6, null, 8 },
new Object[] { null, null, null, null, null, null, null,
null },
new Object[] { null, 3, "def", 5, "ghi", 7, null, 8 },
new Object[] { "a", 2, "b", 4, "c", 6, "d", 8 }, },
new Object[][] {
new Object[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15 },
new Object[] { null, null, null, null, null, null, null, 8,
null, null, null, null, 13, null, 15 }, },
new Object[][] {
new Object[] { "a", "b", "c", "d", "e", "f", "g", "h", "i",
"j", "k", "l" },
new Object[] { "a", null, "c", null, "e", null, "g", null,
"i", null, "k", null },
new Object[] { null, "b", null, "d", null, "f", null, null,
null, "j", null, "l" },
new Object[] { null, null, null, null, null, null, null,
null, null, null, null, "end" }, new Object[] {}, },
new Object[][] { new Object[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, "foo", 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
"bar", 26, 27 }
}, };
private static int fieldNameCounter = 0;
private static String n() {
return "C" + (++fieldNameCounter);
}
public void testComputeFieldLocations1() throws Exception
{
SchemaFactory schemaFactory = new SchemaFactory("schema");
for (int def = 0; def < TEST_CASES.length; def++) {
String[] ddl = TEST_CASES[def];
AkibanInformationSchema ais = schemaFactory.aisWithRowDefs(ddl);
RowDef rowDef = ais.getTable("schema", "test").rowDef();
FieldDef[] fieldDefs = rowDef.getFieldDefs();
if (VERBOSE) {
System.out.println(rowDef);
}
for (int data = 0; data < DATA_CASES[def].length; data++) {
final RowData rowData = new RowData(new byte[1024]);
rowData.reset(2, 1000); // make sure we can handle non-zero
// offset
rowData.createRow(rowDef, DATA_CASES[def][data]);
if (VERBOSE) {
System.out.println("From data: "
+ Arrays.asList(DATA_CASES[def][data]));
System.out.println("Def " + def + " Data " + data);
System.out.println("RowData:\n");
System.out.println(AkServerUtil.dump(rowData.getBytes(),
rowData.getRowStart(), rowData.getRowEnd()
- rowData.getRowStart()));
}
for (int i = 0; i < fieldDefs.length; i++) {
final long location = rowDef.fieldLocation(rowData, i);
if (VERBOSE) {
System.out.println(String.format(
"Field# %3d offset %4d width %4d", i,
location & 0xFFFFFFFFL, location >>> 32));
}
final FieldDef fieldDef = fieldDefs[i];
Object value = i < DATA_CASES[def][data].length ? DATA_CASES[def][data][i]
: null;
assertValuesAreEqual(value, fieldDef, rowData, location);
}
}
}
}
// public void dontComputeFieldLocations2() throws Exception {
// final int fieldCount = 37;
// final Random random = new Random();
// final byte[] stringBytes = new byte[100000];
// for (int i = 0; i < stringBytes.length; i++) {
// stringBytes[i] = (byte) ((i % 26) + 97);
// }
// final FieldDef[] fieldDefs = new FieldDef[fieldCount];
// final Types[] allTypes = Types.values();
// int maxSize = 20;
// for (int i = 0; i < fieldDefs.length; i++) {
// Types type = allTypes[random.nextInt(allTypes.length)];
// if (type.isFixedWidth()) {
// fieldDefs[i] = new FieldDef(n(), type);
// maxSize += type.getMaxWidth();
// } else {
// int max = Math.min(1000, type.getMaxWidth()
// - type.getMinWidth() + 1);
// int maxWidth = random.nextInt(max) + type.getMinWidth();
// fieldDefs[i] = new FieldDef(n(), type, maxWidth);
// maxSize += maxWidth + 3;
// }
// }
// final RowDef rowDef = new RowDef(fieldCount, fieldDefs);
// if (VERBOSE) {
// System.out.println(rowDef);
// }
//
// final Object[] values = new Object[fieldDefs.length];
// final RowData data = new RowData(new byte[maxSize]);
//
// for (int i = 0; i < fieldDefs.length; i++) {
// Object value = null;
// if (random.nextInt(10) != 0) {
// switch (fieldDefs[i].getType()) {
// case U_TINYINT:
// case TINYINT:
// value = (byte) random.nextInt();
// break;
// case U_SMALLINT:
// case SMALLINT:
// value = (short) random.nextInt();
// break;
// case U_MEDIUMINT:
// case MEDIUMINT:
// value = random.nextInt() & 0xFFFFFF;
// break;
// case FLOAT:
// case U_INT:
// case INT:
// value = random.nextInt();
// break;
// case DOUBLE:
// case U_BIGINT:
// case BIGINT:
// // TODO - currently we don't handle unsigned longs.
// value = random.nextLong() & Long.MAX_VALUE;
// break;
// case VARCHAR:
// case BINCHAR:
// case BINVARCHAR:
// case CHAR:
// int size = random.nextInt(fieldDefs[i].getMaxWidth());
// value = new byte[size];
// System.arraycopy(stringBytes, random.nextInt(20000), value,
// 0, size);
// default:
// }
// values[i] = value;
// }
// }
// data.createRow(rowDef, values);
//
// if (VERBOSE) {
// System.out.println(data);
// }
// for (int i = fieldDefs.length; --i >= 0;) {
// final long location = rowDef.fieldLocation(data, i);
// assertValuesAreEqual(values[i], fieldDefs[i], data, location);
// }
//
// for (int i = 0; i < 100000; i++) {
// final int field = random.nextInt(fieldDefs.length);
// final long location = rowDef.fieldLocation(data, field);
// assertValuesAreEqual(values[field], fieldDefs[field], data,
// location);
// }
//
// long xor = 0;
// int count = 0;
// final long start = System.nanoTime();
// while (System.nanoTime() - start < 1000000000L) {
// for (int k = 0; k < 10000; k++) {
// for (int i = 0; i < fieldCount; i++) {
// final long location = rowDef.fieldLocation(data, i);
// // use the result so that HotSpot doesn't optimize away the
// // call
// xor ^= location;
// }
// count += fieldDefs.length;
// }
// }
// final long elapsed = System.nanoTime() - start;
// System.out.println(String.format("Average fieldLocation time on table "
// + "%d columns wide: %dns (xor=%d)", fieldDefs.length, elapsed
// / count, xor));
// }
private void assertValuesAreEqual(final Object value,
final FieldDef fieldDef, final RowData rowData, final long location) {
if (value == null) {
assertEquals(0, location);
}
if (location == 0) {
assertNull(value);
} else if (fieldDef.isFixedSize()) {
long decodedValue = AkServerUtil.getSignedIntegerByWidth(rowData
.getBytes(), (int) location, (int) (location >>> 32));
assertEquals(((Number) value).longValue(), decodedValue);
} else if (value instanceof String) {
final String decodedString = rowData.getStringValue((int) location,
(int) (location >>> 32), fieldDef);
assertEquals(value, decodedString);
} else {
int prefix = fieldDef.getPrefixSize();
byte[] decodedBytes = new byte[((int) (location >>> 32)) - prefix];
System.arraycopy(rowData.getBytes(), (int) location + prefix,
decodedBytes, 0, decodedBytes.length);
assertTrue(Arrays.equals((byte[]) value, decodedBytes));
}
}
}