Merge pull request #1572 from youtube/caseInsensitiveMap

Case Insensitive Map for getting Column Index
This commit is contained in:
Anthony Yeh 2016-04-04 15:13:36 -07:00
Родитель b423c73ce6 39ec949e15
Коммит 6b7efe52ef
4 изменённых файлов: 83 добавлений и 13 удалений

Просмотреть файл

@ -20,6 +20,11 @@
<artifactId>jsr305</artifactId> <artifactId>jsr305</artifactId>
<version>3.0.0</version> <version>3.0.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
</dependencies> </dependencies>
<pluginRepositories> <pluginRepositories>

Просмотреть файл

@ -4,10 +4,11 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.youtube.vitess.proto.Query.Field; import com.youtube.vitess.proto.Query.Field;
import org.apache.commons.collections4.map.CaseInsensitiveMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -16,6 +17,9 @@ import javax.annotation.Nullable;
/** /**
* A wrapper for {@code List<Field>} that also facilitates lookup by field name. * A wrapper for {@code List<Field>} that also facilitates lookup by field name.
* *
* <p>Lookups by field name are case-insensitive, as in {@link java.sql.ResultSet}.
* If multiple fields have the same name, the earliest one will be returned.
*
* <p>The field name maps to an index, rather than a Field, because that same * <p>The field name maps to an index, rather than a Field, because that same
* index is also used to find the value in a separate list. * index is also used to find the value in a separate list.
*/ */
@ -26,25 +30,43 @@ public class FieldMap {
public FieldMap(Iterable<Field> fields) { public FieldMap(Iterable<Field> fields) {
this.fields = ImmutableList.copyOf(checkNotNull(fields)); this.fields = ImmutableList.copyOf(checkNotNull(fields));
ImmutableMap.Builder<String, Integer> builder = new ImmutableMap.Builder<>(); indexMap = new CaseInsensitiveMap<String, Integer>();
// columnIndex is 1-based. // columnIndex is 1-based.
int columnIndex = 1; int columnIndex = 1;
for (Field field : this.fields) { for (Field field : this.fields) {
builder.put(field.getName(), columnIndex++); String columnLabel = field.getName();
// If multiple columns have the same name,
// prefer the earlier one as JDBC ResultSet does.
if (!indexMap.containsKey(columnLabel)) {
indexMap.put(columnLabel, columnIndex);
}
++columnIndex;
} }
indexMap = builder.build();
} }
public List<Field> getList() { public List<Field> getList() {
return fields; return fields;
} }
/**
* Returns the {@link Field} for a 1-based column index.
*
* @param columnIndex 1-based column number (0 is invalid)
*/
public Field get(int columnIndex) { public Field get(int columnIndex) {
// columnIndex is 1-based. // columnIndex is 1-based.
checkArgument(columnIndex >= 1, "columnIndex out of range: %s", columnIndex); checkArgument(columnIndex >= 1, "columnIndex out of range: %s", columnIndex);
return fields.get(columnIndex - 1); return fields.get(columnIndex - 1);
} }
/**
* Returns the 1-based index for a column label.
*
* <p>If multiple columns have the same label,
* the earlier one is returned.
*
* @param columnLabel case-insensitive column label
*/
@Nullable @Nullable
public Integer getIndex(String columnLabel) { public Integer getIndex(String columnLabel) {
return indexMap.get(columnLabel); return indexMap.get(columnLabel);

Просмотреть файл

@ -33,10 +33,12 @@ import javax.annotation.concurrent.NotThreadSafe;
* the list of {@link Field}s from the corresponding * the list of {@link Field}s from the corresponding
* {@link com.youtube.vitess.proto.Query.QueryResult}. * {@link com.youtube.vitess.proto.Query.QueryResult}.
* *
* <p>Note that {@code columnIndex} values start at 1 for the first column, * <p>Methods on {@code Row} are intended to be compatible with those on
* since the methods on {@code Row} are intended to be compatible with those * {@link java.sql.ResultSet} where possible.
* on {@link java.sql.ResultSet} (which uses 1-based {@code columnIndex}) * This means {@code columnIndex} values start at 1 for the first column,
* where possible. * and {@code columnLabel} values are case-insensitive. If multiple columns
* have the same case-insensitive {@code columnLabel}, the earliest one will
* be returned.
*/ */
@NotThreadSafe @NotThreadSafe
public class Row { public class Row {
@ -115,6 +117,8 @@ public class Row {
/** /**
* Returns 1-based column number. * Returns 1-based column number.
*
* @param columnLabel case-insensitive column label
*/ */
public int findColumn(String columnLabel) throws SQLException { public int findColumn(String columnLabel) throws SQLException {
Integer columnIndex = fieldMap.getIndex(columnLabel); Integer columnIndex = fieldMap.getIndex(columnLabel);
@ -124,6 +128,9 @@ public class Row {
return columnIndex; return columnIndex;
} }
/**
* @param columnLabel case-insensitive column label
*/
public Object getObject(String columnLabel) throws SQLException { public Object getObject(String columnLabel) throws SQLException {
return getObject(findColumn(columnLabel)); return getObject(findColumn(columnLabel));
} }
@ -161,6 +168,8 @@ public class Row {
* *
* <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()} * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
* or {@link #getObject(String,Class)}. * or {@link #getObject(String,Class)}.
*
* @param columnLabel case-insensitive column label
*/ */
public int getInt(String columnLabel) throws SQLException { public int getInt(String columnLabel) throws SQLException {
return getInt(findColumn(columnLabel)); return getInt(findColumn(columnLabel));
@ -179,6 +188,9 @@ public class Row {
return value == null ? 0 : value; return value == null ? 0 : value;
} }
/**
* @param columnLabel case-insensitive column label
*/
public UnsignedLong getULong(String columnLabel) throws SQLException { public UnsignedLong getULong(String columnLabel) throws SQLException {
return getULong(findColumn(columnLabel)); return getULong(findColumn(columnLabel));
} }
@ -190,6 +202,9 @@ public class Row {
return getObject(columnIndex, UnsignedLong.class); return getObject(columnIndex, UnsignedLong.class);
} }
/**
* @param columnLabel case-insensitive column label
*/
public String getString(String columnLabel) throws SQLException { public String getString(String columnLabel) throws SQLException {
return getString(findColumn(columnLabel)); return getString(findColumn(columnLabel));
} }
@ -206,6 +221,8 @@ public class Row {
* *
* <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()} * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
* or {@link #getObject(String,Class)}. * or {@link #getObject(String,Class)}.
*
* @param columnLabel case-insensitive column label
*/ */
public long getLong(String columnLabel) throws SQLException { public long getLong(String columnLabel) throws SQLException {
return getLong(findColumn(columnLabel)); return getLong(findColumn(columnLabel));
@ -229,6 +246,8 @@ public class Row {
* *
* <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()} * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
* or {@link #getObject(String,Class)}. * or {@link #getObject(String,Class)}.
*
* @param columnLabel case-insensitive column label
*/ */
public double getDouble(String columnLabel) throws SQLException { public double getDouble(String columnLabel) throws SQLException {
return getDouble(findColumn(columnLabel)); return getDouble(findColumn(columnLabel));
@ -252,6 +271,8 @@ public class Row {
* *
* <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()} * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
* or {@link #getObject(String,Class)}. * or {@link #getObject(String,Class)}.
*
* @param columnLabel case-insensitive column label
*/ */
public float getFloat(String columnLabel) throws SQLException { public float getFloat(String columnLabel) throws SQLException {
return getFloat(findColumn(columnLabel)); return getFloat(findColumn(columnLabel));
@ -272,6 +293,8 @@ public class Row {
/** /**
* Returns the column value as a {@link Date} with the default time zone. * Returns the column value as a {@link Date} with the default time zone.
*
* @param columnLabel case-insensitive column label
*/ */
public Date getDate(String columnLabel) throws SQLException { public Date getDate(String columnLabel) throws SQLException {
return getDate(findColumn(columnLabel), Calendar.getInstance()); return getDate(findColumn(columnLabel), Calendar.getInstance());
@ -279,6 +302,8 @@ public class Row {
/** /**
* Returns the column value as a {@link Date} with the given {@link Calendar}. * Returns the column value as a {@link Date} with the given {@link Calendar}.
*
* @param columnLabel case-insensitive column label
*/ */
public Date getDate(String columnLabel, Calendar cal) throws SQLException { public Date getDate(String columnLabel, Calendar cal) throws SQLException {
return getDate(findColumn(columnLabel), cal); return getDate(findColumn(columnLabel), cal);
@ -317,6 +342,8 @@ public class Row {
/** /**
* Returns the column value as {@link Time} with the default time zone. * Returns the column value as {@link Time} with the default time zone.
*
* @param columnLabel case-insensitive column label
*/ */
public Time getTime(String columnLabel) throws SQLException { public Time getTime(String columnLabel) throws SQLException {
return getTime(findColumn(columnLabel), Calendar.getInstance()); return getTime(findColumn(columnLabel), Calendar.getInstance());
@ -324,6 +351,8 @@ public class Row {
/** /**
* Returns the column value as {@link Time} with the given {@link Calendar}. * Returns the column value as {@link Time} with the given {@link Calendar}.
*
* @param columnLabel case-insensitive column label
*/ */
public Time getTime(String columnLabel, Calendar cal) throws SQLException { public Time getTime(String columnLabel, Calendar cal) throws SQLException {
return getTime(findColumn(columnLabel), cal); return getTime(findColumn(columnLabel), cal);
@ -362,6 +391,8 @@ public class Row {
/** /**
* Returns the column value as {@link Timestamp} with the default time zone. * Returns the column value as {@link Timestamp} with the default time zone.
*
* @param columnLabel case-insensitive column label
*/ */
public Timestamp getTimestamp(String columnLabel) throws SQLException { public Timestamp getTimestamp(String columnLabel) throws SQLException {
return getTimestamp(findColumn(columnLabel), Calendar.getInstance()); return getTimestamp(findColumn(columnLabel), Calendar.getInstance());
@ -369,6 +400,8 @@ public class Row {
/** /**
* Returns the column value as {@link Timestamp} with the given {@link Calendar}. * Returns the column value as {@link Timestamp} with the given {@link Calendar}.
*
* @param columnLabel case-insensitive column label
*/ */
public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException {
return getTimestamp(findColumn(columnLabel), cal); return getTimestamp(findColumn(columnLabel), cal);
@ -410,6 +443,9 @@ public class Row {
} }
} }
/**
* @param columnLabel case-insensitive column label
*/
public byte[] getBytes(String columnLabel) throws SQLException { public byte[] getBytes(String columnLabel) throws SQLException {
return getBytes(findColumn(columnLabel)); return getBytes(findColumn(columnLabel));
} }
@ -421,6 +457,9 @@ public class Row {
return getObject(columnIndex, byte[].class); return getObject(columnIndex, byte[].class);
} }
/**
* @param columnLabel case-insensitive column label
*/
public BigDecimal getBigDecimal(String columnLabel) throws SQLException { public BigDecimal getBigDecimal(String columnLabel) throws SQLException {
return getBigDecimal(findColumn(columnLabel)); return getBigDecimal(findColumn(columnLabel));
} }
@ -437,6 +476,8 @@ public class Row {
* *
* <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()} * <p>To distinguish between 0 and SQL NULL, use either {@link #wasNull()}
* or {@link #getObject(String,Class)}. * or {@link #getObject(String,Class)}.
*
* @param columnLabel case-insensitive column label
*/ */
public short getShort(String columnLabel) throws SQLException { public short getShort(String columnLabel) throws SQLException {
return getShort(findColumn(columnLabel)); return getShort(findColumn(columnLabel));
@ -494,6 +535,7 @@ public class Row {
* } * }
* </pre></blockquote> * </pre></blockquote>
* *
* @param columnLabel case-insensitive column label
* @throws SQLDataException if the type doesn't match the actual value. * @throws SQLDataException if the type doesn't match the actual value.
*/ */
public <T> T getObject(String columnLabel, Class<T> type) throws SQLException { public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {

Просмотреть файл

@ -32,12 +32,13 @@ public class CursorTest {
new SimpleCursor( new SimpleCursor(
QueryResult.newBuilder() QueryResult.newBuilder()
.addFields(Field.newBuilder().setName("col1").build()) .addFields(Field.newBuilder().setName("col1").build())
.addFields(Field.newBuilder().setName("col2").build()) .addFields(Field.newBuilder().setName("COL2").build()) // case-insensitive
.addFields(Field.newBuilder().setName("col3").build()) .addFields(Field.newBuilder().setName("col1").build()) // duplicate
.addFields(Field.newBuilder().setName("col4").build()) // skip duplicate
.build())) { .build())) {
Assert.assertEquals(1, cursor.findColumn("col1")); Assert.assertEquals(1, cursor.findColumn("col1")); // should return first col1
Assert.assertEquals(2, cursor.findColumn("col2")); Assert.assertEquals(2, cursor.findColumn("Col2")); // should be case-insensitive
Assert.assertEquals(3, cursor.findColumn("col3")); Assert.assertEquals(4, cursor.findColumn("col4")); // index should skip over duplicate
} }
} }