/** * 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.api.dml.scan; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.List; import java.util.TreeMap; import com.foundationdb.server.rowdata.RowData; import com.foundationdb.server.rowdata.RowDef; import com.foundationdb.server.rowdata.SchemaFactory; import org.junit.Test; public final class NiceRowTest { @Test public void toRowDataBasic() throws Exception { RowDef rowDef = createRowDef(2); Object[] objects = new Object[2]; objects[0] = 5; objects[1] = "Bob"; RowData rowData = create(rowDef, objects); NewRow newRow = NiceRow.fromRowData(rowData, rowDef); // Why -1: because an __akiban_pk column gets added assertEquals("fields count", 2, newRow.getFields().size() - 1); assertEquals("field[0]", 5, newRow.get(0)); assertEquals("field[1]", "Bob", newRow.get(1)); compareRowDatas(rowData, newRow.toRowData()); } @Test public void toRowDataLarge() throws Exception { final int NUM = 30; RowDef rowDef = createRowDef(NUM); Object[] objects = new Object[NUM]; objects[0] = 15; objects[1] = "Robert"; for (int i=2; i < NUM; ++i) { objects[i] = i + 1000; } RowData rowData = create(rowDef, objects); NewRow newRow = NiceRow.fromRowData(rowData, rowDef); // Why -1: because an __akiban_pk column gets added assertEquals("fields count", NUM, newRow.getFields().size() - 1); assertEquals("field[0]", 15, newRow.get(0)); assertEquals("field[1]", "Robert", newRow.get(1)); for (int i=2; i < NUM; ++i) { int expected = i + 1000; assertEquals("field[1]", expected, newRow.get(i)); } compareRowDatas(rowData, newRow.toRowData()); } @Test public void toRowDataSparse() throws Exception { final int NUM = 30; RowDef rowDef = createRowDef(NUM); Object[] objects = new Object[NUM]; objects[0] = 15; objects[1] = "Robert"; int nulls = 0; for (int i=2; i < NUM; ++i) { if ( (i % 3) == 0) { ++nulls; } else { objects[i] = i + 1000; } } assertTrue("nulls==0", nulls > 0); RowData rowData = create(rowDef, objects); NewRow newRow = NiceRow.fromRowData(rowData, rowDef); // Why -1: because an __akiban_pk column gets added assertEquals("fields count", NUM, newRow.getFields().size() - 1); assertEquals("field[0]", 15, newRow.get(0)); assertEquals("field[1]", "Robert", newRow.get(1)); for (int i=2; i < NUM; ++i) { Integer expected = (i % 3) == 0 ? null : i + 1000; assertEquals("field[1]", expected, newRow.get(i)); } compareRowDatas(rowData, newRow.toRowData()); } @Test public void testEquality() { TreeMap<Integer,NiceRow> mapOne = new TreeMap<>(); TreeMap<Integer,NiceRow> mapTwo = new TreeMap<>(); NiceRow rowOne = new NiceRow(1, (RowDef)null); rowOne.put(0, Long.valueOf(0l)); rowOne.put(1, "hello world"); mapOne.put(0, rowOne); NiceRow rowTwo = new NiceRow(1, (RowDef)null); rowTwo.put(0, Long.valueOf(0l)); rowTwo.put(1, "hello world"); mapTwo.put(0, rowTwo); assertEquals("rows", rowOne, rowTwo); assertEquals("maps", mapOne, mapTwo); } @Test public void toRowDataOneByteUTF8() throws Exception { final int BYTE_COUNT = 0x7F; byte[] bytes = new byte[BYTE_COUNT]; for(int i = 0; i < BYTE_COUNT; ++i) { bytes[i] = (byte)i; } String str = new String(bytes, "utf8"); String ddl = "create table test.t(id int not null primary key, v varchar(255) character set utf8)"; RowDef rowDef = SCHEMA_FACTORY.aisWithRowDefs(ddl).getTable("test", "t").rowDef(); Object[] objects = { 1, str }; RowData rowData = create(rowDef, objects); NewRow newRow = NiceRow.fromRowData(rowData, rowDef); assertEquals("fields count", 2, newRow.getFields().size()); assertEquals("field[0]", 1, newRow.get(0)); assertEquals("field[1]", str, newRow.get(1)); assertEquals("field[1] charset", "UTF8", rowDef.getFieldDef(1).column().getCharsetName()); compareRowDatas(rowData, newRow.toRowData()); } // bug1057016 @Test public void toRowDataStringWithSurrogatePairs() throws Exception { final String TEST_STR = "abc \ud83d\ude0d def"; assertEquals("string length", 10, TEST_STR.length()); assertEquals("char 4 high surrogate", true, Character.isHighSurrogate(TEST_STR.charAt(4))); assertEquals("char 5 low surrogate", true, Character.isLowSurrogate(TEST_STR.charAt(5))); assertEquals("utf8 byte length", 12, TEST_STR.getBytes("UTF-8").length); String ddl = "create table test.t(id int not null primary key, v varchar(32) character set utf8)"; RowDef rowDef = SCHEMA_FACTORY.aisWithRowDefs(ddl).getTable("test", "t").rowDef(); Object[] objects = { 1, TEST_STR }; RowData rowData = create(rowDef, objects); NewRow newRow = NiceRow.fromRowData(rowData, rowDef); assertEquals("fields count", 2, newRow.getFields().size()); assertEquals("field[0]", 1, newRow.get(0)); assertEquals("field[1]", TEST_STR, newRow.get(1)); assertEquals("field[1] charset", "UTF8", rowDef.getFieldDef(1).column().getCharsetName()); compareRowDatas(rowData, newRow.toRowData()); } private static byte[] bytes() { return new byte[1024]; } private static RowDef createRowDef(int totalColumns) throws Exception { assertTrue("bad totalColumns=" + totalColumns, totalColumns >= 2); String[] ddl = new String[totalColumns + 2]; int i = 0; ddl[i++] = "create table test_table("; ddl[i++] = "id int"; ddl[i++] = ", name varchar(128)"; for (int c = 2; c < totalColumns; c++) { ddl[i++] = String.format(", field_%s int", c); } ddl[i] = ");"; return SCHEMA_FACTORY.aisWithRowDefs(ddl).getTable("test_schema", "test_table").rowDef(); } private RowData create(RowDef rowDef, Object[] objects) { RowData rowData = new RowData(bytes()); rowData.createRow(rowDef, objects); assertEquals("start", 0, rowData.getBufferStart()); assertEquals("end and length", rowData.getBufferEnd(), rowData.getBufferLength()); return rowData; } private void compareRowDatas(RowData expected, RowData actual) { if (expected == actual) { return; } List<Byte> expectedBytes = byteListFor(expected); List<Byte> actualBytes = byteListFor(actual); assertEquals("bytes", expectedBytes, actualBytes); } private List<Byte> byteListFor(RowData rowData) { byte[] bytes = rowData.getBytes(); assertNotNull("RowData bytes[] null", bytes); assertTrue("start < 0: " + rowData.getRowStart(), rowData.getRowStart() >= 0); assertTrue("end out of range: " + rowData.getRowEnd(), rowData.getRowEnd() <= bytes.length); List<Byte> bytesList = new ArrayList<>(); for (int i=rowData.getBufferStart(), MAX=rowData.getRowEnd(); i < MAX; ++i) { bytesList.add(bytes[i]); } return bytesList; } private static final SchemaFactory SCHEMA_FACTORY = new SchemaFactory("test_schema"); }