implements getXXX methods to return values of the correct types. rearranges the JdbcDriverTest. renames SchemaDecoder. patch by gdusbabek, reviewed by eevans. CASSANDRA-2124

git-svn-id: https://svn.apache.org/repos/asf/cassandra/trunk@1079975 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Gary Dusbabek 2011-03-09 20:23:10 +00:00
parent 07ca8f6196
commit 6edca314f0
8 changed files with 259 additions and 144 deletions

View File

@ -11,6 +11,7 @@ import org.apache.cassandra.db.marshal.TimeUUIDType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.thrift.CfDef;
import org.apache.cassandra.thrift.KsDef;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.UUIDGen;
import org.slf4j.Logger;
@ -23,9 +24,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SchemaDecoder
public class ColumnDecoder
{
private static final Logger logger = LoggerFactory.getLogger(SchemaDecoder.class);
private static final Logger logger = LoggerFactory.getLogger(ColumnDecoder.class);
private static final String MapFormatString = "%s.%s.%s";
enum Specifier
@ -39,7 +40,7 @@ public class SchemaDecoder
// cache the comparators for efficiency.
private Map<String, AbstractType> comparators = new HashMap<String, AbstractType>();
public SchemaDecoder(List<KsDef> defs)
public ColumnDecoder(List<KsDef> defs)
{
for (KsDef ks : defs)
for (CfDef cf : ks.getCf_defs())
@ -99,6 +100,15 @@ public class SchemaDecoder
return comparator.getString(ByteBuffer.wrap(name));
}
public static String colValueAsString(Object value) {
if (value instanceof String)
return (String)value;
else if (value instanceof byte[])
return ByteBufferUtil.bytesToHex(ByteBuffer.wrap((byte[])value));
else
return value.toString();
}
public Col makeCol(String keyspace, String columnFamily, byte[] name, byte[] value)
{
CfDef cfDef = cfDefs.get(String.format("%s.%s", keyspace, columnFamily));

View File

@ -64,7 +64,7 @@ public class Connection
// todo: encapsulate.
public String curKeyspace;
public String curColumnFamily;
public SchemaDecoder decoder;
public ColumnDecoder decoder;
/**
* Create a new <code>Connection</code> instance.
@ -137,7 +137,7 @@ public class Connection
throws InvalidRequestException, UnavailableException, TimedOutException, TException
{
if (decoder == null)
decoder = new SchemaDecoder(client.describe_keyspaces());
decoder = new ColumnDecoder(client.describe_keyspaces());
Matcher isKeyspace = KeyspacePattern.matcher(queryStr);
if (isKeyspace.matches())

View File

@ -3,11 +3,11 @@ package org.apache.cassandra.cql.driver;
public class Results
{
private final SchemaDecoder decoder;
private final ColumnDecoder decoder;
private final String keyspace;
private final String columnFamily;
public Results(SchemaDecoder decoder, String keyspace, String columnFamily)
public Results(ColumnDecoder decoder, String keyspace, String columnFamily)
{
this.decoder = decoder;
this.keyspace = keyspace;

View File

@ -23,6 +23,7 @@ package org.apache.cassandra.cql.driver.jdbc;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
@ -47,8 +48,7 @@ import java.util.Map;
import java.util.WeakHashMap;
import org.apache.cassandra.cql.driver.Col;
import org.apache.cassandra.cql.driver.Results;
import org.apache.cassandra.cql.driver.SchemaDecoder;
import org.apache.cassandra.cql.driver.ColumnDecoder;
import org.apache.cassandra.thrift.Column;
import org.apache.cassandra.thrift.CqlResult;
import org.apache.cassandra.thrift.CqlRow;
@ -62,7 +62,7 @@ class CassandraResultSet implements ResultSet
/** The r set. */
private final CqlResult rSet;
private final SchemaDecoder decoder;
private final ColumnDecoder decoder;
private final String keyspace;
private final String columnFamily;
@ -83,7 +83,7 @@ class CassandraResultSet implements ResultSet
*
* @param resultSet the result set
*/
CassandraResultSet(CqlResult resultSet, SchemaDecoder decoder, String keyspace, String columnFamily)
CassandraResultSet(CqlResult resultSet, ColumnDecoder decoder, String keyspace, String columnFamily)
{
this.rSet = resultSet;
this.decoder = decoder;
@ -356,23 +356,24 @@ class CassandraResultSet implements ResultSet
}
/**
* @param arg0
* @param index
* @return
* @throws SQLException
*/
public byte[] getBytes(int arg0) throws SQLException
public byte[] getBytes(int index) throws SQLException
{
throw new UnsupportedOperationException("method not supported");
return values.get(index) != null ? (byte[])values.get(index).getValue() : null;
}
/**
* @param arg0
* @param name
* @return
* @throws SQLException
*/
public byte[] getBytes(String arg0) throws SQLException
public byte[] getBytes(String name) throws SQLException
{
throw new UnsupportedOperationException("method not supported");
String nameAsString = decoder.colNameAsString(keyspace, columnFamily, name);
return valueMap.get(nameAsString) != null ? (byte[])valueMap.get(nameAsString) : null;
}
/**
@ -543,43 +544,45 @@ class CassandraResultSet implements ResultSet
}
/**
* @param arg0
* @param index
* @return
* @throws SQLException
*/
public int getInt(int arg0) throws SQLException
public int getInt(int index) throws SQLException
{
throw new UnsupportedOperationException("method not supported");
return values.get(index) != null ? ((BigInteger)values.get(index).getValue()).intValue() : null;
}
/**
* @param arg0
* @param name
* @return
* @throws SQLException
*/
public int getInt(String arg0) throws SQLException
public int getInt(String name) throws SQLException
{
throw new UnsupportedOperationException("method not supported");
String nameAsString = decoder.colNameAsString(keyspace, columnFamily, name);
return valueMap.get(nameAsString) != null ? ((BigInteger)valueMap.get(nameAsString)).intValue() : null;
}
/**
* @param arg0
* @param index
* @return
* @throws SQLException
*/
public long getLong(int arg0) throws SQLException
public long getLong(int index) throws SQLException
{
throw new UnsupportedOperationException("method not supported");
return values.get(index) != null ? (Long)values.get(index).getValue() : null;
}
/**
* @param arg0
* @param name
* @return
* @throws SQLException
*/
public long getLong(String arg0) throws SQLException
public long getLong(String name) throws SQLException
{
throw new UnsupportedOperationException("method not supported");
String nameAsString = decoder.colNameAsString(keyspace, columnFamily, name);
return valueMap.get(nameAsString) != null ? (Long)valueMap.get(nameAsString) : null;
}
/**
@ -652,23 +655,24 @@ class CassandraResultSet implements ResultSet
}
/**
* @param arg0
* @param index
* @return
* @throws SQLException
*/
public Object getObject(int arg0) throws SQLException
public Object getObject(int index) throws SQLException
{
throw new UnsupportedOperationException("method not supported");
return values.get(index) == null ? null : values.get(index).getValue();
}
/**
* @param arg0
* @param name
* @return
* @throws SQLException
*/
public Object getObject(String arg0) throws SQLException
public Object getObject(String name) throws SQLException
{
throw new UnsupportedOperationException("method not supported");
String nameAsString = decoder.colNameAsString(keyspace, columnFamily, name);
return valueMap.get(nameAsString);
}
/**
@ -798,7 +802,7 @@ class CassandraResultSet implements ResultSet
*/
public String getString(int index) throws SQLException
{
return values.get(index) != null ? values.get(index).getValue().toString() : null;
return values.get(index) != null ? ColumnDecoder.colValueAsString(values.get(index).getValue()) : null;
}
/**
@ -809,7 +813,7 @@ class CassandraResultSet implements ResultSet
public String getString(String name) throws SQLException
{
String nameAsString = this.decoder.colNameAsString(this.keyspace, this.columnFamily, name);
return valueMap.get(nameAsString) != null ? valueMap.get(nameAsString).toString() : null;
return valueMap.get(nameAsString) != null ? ColumnDecoder.colValueAsString(valueMap.get(nameAsString)) : null;
}
/**

View File

@ -171,11 +171,11 @@ class CassandraStatement implements PreparedStatement
}
catch (InvalidRequestException e)
{
throw new SQLException(e.getMessage());
throw new SQLException(e.getWhy());
}
catch (UnavailableException e)
{
throw new SQLException(e.getMessage());
throw new SQLException("Cassandra was unavialable", e);
}
catch (TimedOutException e)
{

View File

@ -4,8 +4,6 @@ import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import junit.framework.TestCase;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ConfigurationException;
import org.apache.cassandra.config.DatabaseDescriptor;
@ -15,7 +13,7 @@ import org.apache.cassandra.service.EmbeddedCassandraService;
/**
* The abstract BaseClass.
*/
public abstract class EmbeddedServiceBase extends TestCase
public abstract class EmbeddedServiceBase
{
/** The embedded server cassandra. */
@ -27,11 +25,10 @@ public abstract class EmbeddedServiceBase extends TestCase
*
* @throws Exception the exception
*/
protected void startCassandraServer() throws IOException, ConfigurationException
static void startCassandraServer() throws IOException, ConfigurationException
{
if (!checkIfServerRunning())
{
System.setProperty("cassandra.config", "cassandra.yaml");
loadData();
cassandra = new EmbeddedCassandraService();
cassandra.start();
@ -44,7 +41,7 @@ public abstract class EmbeddedServiceBase extends TestCase
*
* @throws ConfigurationException the configuration exception
*/
private void loadData() throws ConfigurationException
static void loadData() throws ConfigurationException
{
for (KSMetaData table : DatabaseDescriptor.readTablesFromYaml())
{
@ -60,7 +57,7 @@ public abstract class EmbeddedServiceBase extends TestCase
*
* @return true, if successful
*/
private boolean checkIfServerRunning()
static boolean checkIfServerRunning()
{
try
{

View File

@ -20,7 +20,9 @@ package org.apache.cassandra.cql.driver.jdbc;
*
*/
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.FloatBuffer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
@ -28,7 +30,9 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.cassandra.config.ConfigurationException;
import org.apache.cassandra.utils.FBUtilities;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
/**
@ -36,134 +40,210 @@ import org.junit.Test;
*/
public class JdbcDriverTest extends EmbeddedServiceBase
{
private java.sql.Connection con = null;
private static java.sql.Connection con = null;
private static final String first = FBUtilities.bytesToHex("first".getBytes());
private static final String firstrec = FBUtilities.bytesToHex("firstrec".getBytes());
private static final String last = FBUtilities.bytesToHex("last".getBytes());
private static final String lastrec = FBUtilities.bytesToHex("lastrec".getBytes());
/** SetUp */
@Override
protected void setUp()
@BeforeClass
public static void startServer() throws Exception
{
try
startCassandraServer();
Class.forName("org.apache.cassandra.cql.driver.jdbc.CassandraDriver");
con = DriverManager.getConnection("jdbc:cassandra:root/root@localhost:9170/Keyspace1");
String[] inserts =
{
startCassandraServer();
Class.forName("org.apache.cassandra.cql.driver.jdbc.CassandraDriver");
con = DriverManager.getConnection("jdbc:cassandra:root/root@localhost:9170/Keyspace1");
final String updateQ = "UPDATE Standard1 SET \"first\" = \"firstrec\", \"last\" = \"lastrec\" WHERE KEY = \"jsmith\"";
executeNoResults(con, updateQ);
}
catch (ClassNotFoundException e)
{
fail(e.getMessage());
}
catch(IOException ioex)
{
fail(ioex.getMessage());
}
catch (SQLException e)
{
fail(e.getMessage());
}
catch (ConfigurationException e)
{
fail(e.getMessage());
}
// String.format("UPDATE Standard1 SET \"%s\" = \"%s\", \"%s\" = \"%s\" WHERE KEY = \"jsmith\"", first, firstrec, last, lastrec),
"UPDATE JdbcInteger SET 1 = 11, 2 = 22 WHERE KEY = \"jsmith\"",
"UPDATE JdbcInteger SET 3 = 33, 4 = 44 WHERE KEY = \"jsmith\"",
"UPDATE JdbcLong SET 1L = 11L, 2L = 22L WHERE KEY = \"jsmith\"",
"UPDATE JdbcAscii SET \"first\" = \"firstrec\", \"last\" = \"lastrec\" WHERE key = \"jsmith\"",
// String.format("UPDATE JdbcBytes SET \"%s\" = \"%s\", \"%s\" = \"%s\" WHERE key = \"jsmith\"", first, firstrec, last, lastrec),
"UPDATE JdbcUtf8 SET \"first\" = \"firstrec\", \"last\" = \"lastrec\" WHERE key = \"jsmith\"",
};
for (String q : inserts)
executeNoResults(con, q);
}
/** Method to test statement. */
@Test
public void testWithStatement()
public void testWithStatement() throws SQLException
{
try
{
String selectQ = "SELECT \"first\", \"last\" FROM Standard1 WHERE KEY=\"jsmith\"";
Statement stmt = con.createStatement();
scrollResultset(stmt.executeQuery(selectQ), "first", "last");
}
catch (SQLException e)
{
fail(e.getMessage());
}
Statement stmt = con.createStatement();
// String selectQ = String.format("SELECT \"%s\", \"%s\" FROM Standard1 WHERE KEY=\"jsmith\"", first, last);
// checkResultSet(stmt.executeQuery(selectQ), "Bytes", 1, first, last);
String selectQ = "SELECT 1, 2 FROM JdbcInteger WHERE KEY=\"jsmith\"";
checkResultSet(stmt.executeQuery(selectQ), "Int", 1, "1", "2");
selectQ = "SELECT 3, 4 FROM JdbcInteger WHERE KEY=\"jsmith\"";
checkResultSet(stmt.executeQuery(selectQ), "Int", 1, "3", "4");
selectQ = "SELECT 1, 2, 3, 4 FROM JdbcInteger WHERE KEY=\"jsmith\"";
checkResultSet(stmt.executeQuery(selectQ), "Int", 1, "1", "2", "3", "4");
selectQ = "SELECT 1L, 2L FROM JdbcLong WHERE KEY=\"jsmith\"";
checkResultSet(stmt.executeQuery(selectQ), "Long", 1, "1", "2");
selectQ = "SELECT \"first\", \"last\" FROM JdbcAscii WHERE KEY=\"jsmith\"";
checkResultSet(stmt.executeQuery(selectQ), "String", 1, "first", "last");
// selectQ = String.format("SELECT \"%s\", \"%s\" FROM JdbcBytes WHERE KEY=\"jsmith\"", first, last);
// checkResultSet(stmt.executeQuery(selectQ), "Bytes", 1, first, last);
selectQ = "SELECT \"first\", \"last\" FROM JdbcUtf8 WHERE KEY=\"jsmith\"";
checkResultSet(stmt.executeQuery(selectQ), "String", 1, "first", "last");
}
/** Method to test with prepared statement.*/
@Test
public void testWithPreparedStatement()
public void testWithPreparedStatement() throws SQLException
{
try
{
final String selectQ = "SELECT \"first\", \"last\" FROM Standard1 WHERE KEY=\"jsmith\"";
scrollResultset(executePreparedStatementWithResults(con, selectQ), "first", "last");
}
catch (SQLException e)
{
fail(e.getMessage());
}
tearDown();
}
/** Method to test with update statement.*/
@Test
public void testWithUpdateStatement()
{
try
{
final String updateQ = "UPDATE Standard1 SET \"firstN\" = \"jdbc\", \"lastN\" = \"m\" WHERE KEY = \"jsmith\"";
executeNoResults(con, updateQ);
final String updateSelect = "SELECT \"firstN\", \"lastN\" FROM Standard1 WHERE KEY=\"jsmith\"";
scrollResultset(executePreparedStatementWithResults(con, updateSelect), "firstN", "lastN");
}
catch (SQLException e)
{
fail(e.getMessage());
}
// String selectQ = "SELECT \"first\", \"last\" FROM Standard1 WHERE KEY=\"jsmith\"";
// checkResultSet(executePreparedStatementWithResults(con, selectQ), "Bytes", 1, "first", "last");
String selectQ = "SELECT 1, 2 FROM JdbcInteger WHERE KEY=\"jsmith\"";
checkResultSet(executePreparedStatementWithResults(con, selectQ), "Int", 1, "1", "2");
selectQ = "SELECT 3, 4 FROM JdbcInteger WHERE KEY=\"jsmith\"";
checkResultSet(executePreparedStatementWithResults(con, selectQ), "Int", 1, "3", "4");
selectQ = "SELECT 1, 2, 3, 4 FROM JdbcInteger WHERE KEY=\"jsmith\"";
checkResultSet(executePreparedStatementWithResults(con, selectQ), "Int", 1, "1", "2", "3", "4");
selectQ = "SELECT 1L, 2L FROM JdbcLong WHERE KEY=\"jsmith\"";
checkResultSet(executePreparedStatementWithResults(con, selectQ), "Long", 1, "1", "2");
selectQ = "SELECT \"first\", \"last\" FROM JdbcAscii WHERE KEY=\"jsmith\"";
checkResultSet(executePreparedStatementWithResults(con, selectQ), "String", 1, "first", "last");
// selectQ = "SELECT \"first\", \"last\" FROM JdbcBytes WHERE KEY=\"jsmith\"";
// checkResultSet(executePreparedStatementWithResults(con, selectQ), "Bytes", 1, "first", "last");
selectQ = "SELECT \"first\", \"last\" FROM JdbcUtf8 WHERE KEY=\"jsmith\"";
checkResultSet(executePreparedStatementWithResults(con, selectQ), "String", 1, "first", "last");
}
/* Method to test with Delete statement. */
@Test
public void testWithDeleteStatement()
public void testWithDeleteStatement() throws SQLException
{
try
// the pattern: 0) a deltion, 1) ensure deletion 2) ensure deletion wasn't over-eager.
String[] statements =
{
// Delete
final String deleteQ = "DELETE \"firstN\", \"lastN\" FROM Standard1 WHERE KEY=\"jsmith\"";
executeNoResults(con, deleteQ);
String updateSelect = "SELECT \"firstN\", \"lastN\" FROM Standard1 WHERE KEY=\"jsmith\"";
scrollResultset(executePreparedStatementWithResults(con, updateSelect), "firstN", "lastN");
}
catch (SQLException e)
// "DELETE \"firstN\", \"lastN\" FROM Standard1 WHERE KEY=\"jsmith\"",
// "SELECT \"firstN\", \"lastN\" FROM Standard1 WHERE KEY=\"jsmith\"",
// "SELECT \"first\" FROM Standard1 WHERE KEY=\"jsmith\"",
"DELETE 1, 3 FROM JdbcInteger WHERE KEY=\"jsmith\"",
"SELECT 1, 3 FROM JdbcInteger WHERE KEY=\"jsmith\"", // fails.
"SELECT 2, 4 FROM JdbcInteger WHERE KEY=\"jsmith\"",
"DELETE 1L FROM JdbcLong WHERE KEY=\"jsmith\"",
"SELECT 1L FROM JdbcLong WHERE KEY=\"jsmith\"",
"SELECT 2L FROM JdbcLong WHERE KEY=\"jsmith\"",
"DELETE \"first\" FROM JdbcAscii WHERE KEY=\"jsmith\"",
"SELECT \"first\" FROM JdbcAscii WHERE KEY=\"jsmith\"",
"SELECT \"last\" FROM JdbcAscii WHERE KEY=\"jsmith\"",
// "DELETE \"first\" FROM JdbcBytes WHERE KEY=\"jsmith\"",
// "SELECT \"first\" FROM JdbcBytes WHERE KEY=\"jsmith\"",
// "SELECT \"last\" FROM JdbcBytes WHERE KEY=\"jsmith\"",
"DELETE \"first\" FROM JdbcUtf8 WHERE KEY=\"jsmith\"",
"SELECT \"first\" FROM JdbcUtf8 WHERE KEY=\"jsmith\"",
"SELECT \"last\" FROM JdbcUtf8 WHERE KEY=\"jsmith\"",
};
for (int i = 0; i < statements.length/3; i++)
{
fail(e.getMessage());
executeNoResults(con, statements[3*i]);
ResultSet rs = executePreparedStatementWithResults(con, statements[3*i+1]);
assert !rs.next() : statements[3*i+1];
rs.close();
rs = executePreparedStatementWithResults(con, statements[3*i+2]);
assert rs.next() : statements[3*i+2];
}
}
@Override
protected void tearDown()
@AfterClass
public static void stopServer() throws SQLException
{
try
if (con != null)
{
if (con != null)
String[] stmts =
{
final String updateQ = "TRUNCATE Standard1";
executeNoResults(con, updateQ);
con.close();
con = null;
// "TRUNCATE Standard1",
// "TRUNCATE JcbcAscii", // todo: this one is broken for some reason.
"TRUNCATE JdbcInteger",
"TRUNCATE JdbcLong",
// "TRUNCATE JdbcBytes",
"TRUNCATE JdbcUtf8",
};
for (String stmt : stmts)
{
try
{
executeNoResults(con, stmt);
}
catch (SQLException ex)
{
throw new SQLException(stmt, ex);
}
}
}
catch (SQLException e)
{
fail(e.getMessage());
con.close();
con = null;
}
}
// todo: check expected values as well.
/** iterates over a result set checking columns */
private static void scrollResultset(final ResultSet rSet, final String... columnNames) throws SQLException
private static void checkResultSet(ResultSet rs, String accessor, int expectedRows, String... cols) throws SQLException
{
assertNotNull(rSet);
while (rSet.next())
int actualRows = 0;
assert rs != null;
while (rs.next())
{
assertNotNull(rSet.getString(0));
for (String colName : columnNames)
assertNotNull(rSet.getString(colName));
actualRows++;
for (int c = 0; c < cols.length; c++)
{
// getString and getObject should always work.
assert rs.getString(cols[c]) != null;
assert rs.getString(c) != null;
assert rs.getObject(cols[c]) != null;
assert rs.getObject(c) != null;
// now call the accessor.
try
{
Method byInt = rs.getClass().getDeclaredMethod("get" + accessor, int.class);
byInt.setAccessible(true);
assert byInt.invoke(rs, c) != null;
Method byString = rs.getClass().getDeclaredMethod("get" + accessor, String.class);
byString.setAccessible(true);
assert byString.invoke(rs, cols[c]) != null;
}
catch (NoSuchMethodException ex)
{
throw new RuntimeException(ex);
}
catch (IllegalAccessException ex)
{
throw new RuntimeException(ex);
}
catch (InvocationTargetException ex)
{
throw new RuntimeException(ex);
}
}
}
assert actualRows == expectedRows;
}
/** executes a prepared statement */

View File

@ -33,6 +33,9 @@ encryption_options:
truststore: conf/.truststore
truststore_password: cassandra
incremental_backups: true
# Big Fat Note: as you add new colum families, be sure to append only (no insertions please), lest you break the
# serialization tests which expect columnfamily ids to be constant over time.
keyspaces:
- name: Keyspace1
replica_placement_strategy: org.apache.cassandra.locator.SimpleStrategy
@ -66,7 +69,7 @@ keyspaces:
rows_cached: 0
keys_cached: 0
compare_with: IntegerType
- name: Super1
column_type: Super
compare_subcolumns_with: LongType
@ -119,6 +122,27 @@ keyspaces:
# index will be added dynamically
rows_cached: 0
keys_cached: 0
- name: JdbcInteger
compare_with: IntegerType
default_validation_class: IntegerType
- name: JdbcUtf8
compare_with: UTF8Type
default_validation_class: UTF8Type
- name: JdbcLong
compare_with: LongType
default_validation_class: LongType
- name: JdbcBytes
compare_with: BytesType
default_validation_class: BytesType
- name: JdbcAscii
compare_with: AsciiType
default_validation_class: AsciiType
- name: Keyspace2
replica_placement_strategy: org.apache.cassandra.locator.SimpleStrategy