Cassandra Java Driver BoundStatement.setList works for set as well - cassandra

I have a table userset
create table IF NOT EXISTS userset (id int primary key, name set, phone set, emails list);
Now I am executing an insert statement through datastax java driver : cassandra-driver-core-3.1.0.jar. Now I have a java.util.List of String say listString
List<String> listString = new ArrayList<String>();
listString.add("NewName");
boundStatement.setList(1, listString);
Here boundStatement is an instance of com.datastax.driver.core.BoundStatement. On index 1 i am setting the value of name in userset.
Even though the backend type of name is set and I am using BoundStatement-> setList it still executes without any errors and inputs the value in the name correctly. Is this a functionality of BoundStatement in datastax driver.
Why doesn't it throw an error when I try to setList for a parameter which is a set in the backend server?

You can say it's a bug in the datastax driver.
When you bind data with boundStatement.setList or boundStatement.setSet both method uses lookupCodec method to find the codec with the column type and don't validate the value.
But If you use statement.bind to bind data, it uses findCodec method to find the codec with column type and it's validate with the given value
if ((cqlType == null || cqlType.getName() == LIST) && value instanceof List) {
//...........
}
if ((cqlType == null || cqlType.getName() == SET) && value instanceof Set) {
//............
}

Related

I am getting an InvalidTypeException whenever I am using the row.getToken("fieldname")?

for the following piece of code I am getting an InvalidTypeException whenever I am using the row.getToken("fieldname").
Record RowToRecord(Row rw) {
ColumnDefinitions cd = rw.getColumnDefinitions();
Record rec = new Record();
int i;
for(i = 0; i < cd.size(); i++) {
rec.fields.add(cd.getName(i));
System.out.println(cd.getName(i));
//System.out.println((rw.getToken(cd.getName(i))).getValue());
Token tk = rw.getToken(cd.getName(i)); //// InvalidTypeException on this line.
//System.out.println(tk.getValue()+" "+tk.getType().toString());
rec.values.add(tk.getValue());
rec.types.add(tk.getType().toString());
//Token tk = new Token();
}
return rec;
}
getToken is meant to be called on a column that contains a Cassandra token. In 99% of cases, that will be the result of a call to the token() CQL function, for example the first column in this query:
select token(id), col1 from my_table where id = ...
Your code is calling it for all columns, which will fail as soon as you have a column that doesn't match the CQL type for tokens.
That CQL type depends on the partitioner used in your cluster:
murmur3 partitioner (the default): token(...) will return a BIGINT
random partitioner: VARINT
ordered partitioner: BLOB
In theory you can call getToken on any column with this type (although in practice it probably only makes sense for columns that are the result of a token() call, as explained above).

Slick: Odd error when using .firstOption.get | [SlickException: Read NULL value (null) for ResultSet column Path s2._5]

So I have the following code used as a validation method:
if (TableQuery[UsersTable].filter(_.name === login).exists.run) {
val id = TableQuery[UsersTable].filter(_.name === login).firstOption.get.id
val name = TableQuery[UsersTable].filter(_.id === id).firstOption.get.name
}
if you're wondering, I check to make sure of .exists before I query the next two times because the login value can be equal to two columns in the database.
Anyways, I get [SlickException: Read NULL value (null) for ResultSet column Path s2._5] when attempting to get the id above, and I'm unsure why. There should be a first option there because the code has already validated a row exists for the requirements typed beforehand. No "id" column values are null.
How can I get this id value working correctly?
One of the involved columns is nullable but you didn't specify it as Option[...] in the Table.

Setting a NULL value in a BoundStatement

I'm using Cassandra Driver 2.0.0-beta2 with Cassandra 2.0.1.
I want to set a NULL value to a column of type 'int', in a BoundStatement. I don't think I can with setInt.
This is the code I'm using:
String insertStatementString = "insert into subscribers(subscriber,start_date,subscriber_id)";
PreparedStatement insertStatement = session.prepare(insertStatementString);
BoundStatement bs = new BoundStatement(insertStatement);
bs.setString("subscriber",s.getSubscriberName());
bs.setDate("start_date",startDate);
bs.setInt("subscriber_id",s.getSubscriberID());
The last line throws a null pointer exception, which can be explained because s.getSubscriberID() return an Integer and the BoundStatement accepts only ints, so when the id is null, it can't be converted, thus the exception.
The definition in my opinion should change to:
BoundStatement.setInt(String name, Integer v);
The way it is right now, I can't set NULL values for numbers.
Or am I missing something?
Is there other way to achieve this?
In cqlsh, setting null to a column of type 'int' is possible.
There is no need to bind values where the value will be empty or null. Therefore a null check might be useful, e.g.,
if(null != s.getSubscriberID()){
bs.setInt("subscriber_id",s.getSubscriberID());
}
As to the question of multiple instantiations of BoundStatement, the creation of multiple BoundStatement will be cheap in comparison with PreparedStatements (see the CQL documentation on prepared statements). Therefore the benefit is more clear when you begin to reuse the PreparedStatement, e.g., with a loop
String insertStatementString = "insert into subscribers(subscriber,start_date,subscriber_id)";
PreparedStatement insertStatement = session.prepare(insertStatementString);
// Inside a loop for example
for(Subscriber s: subscribersCollection){
BoundStatement bs = new BoundStatement(insertStatement);
bs.setString("subscriber",s.getSubscriberName());
bs.setDate("start_date",startDate);
if(null != s.getSubscriberID()){
bs.setInt("subscriber_id",s.getSubscriberID());
}
session.execute(bs);
}
I decided not to set the value at all. By default, it is null. It's a weird workaround.
But now I have to instantiate the BoundStatement before every call, because otherwise I risk having a value different than null from a previous call.
It would be great if they added a more comprehensive 'null' support.

subsonic 2.2 does not update nullable columns to null values

When using the Update method on ODSController (ie using GridView / FormView in an ASP.NET application) and passing null values to nullable columns, the nullable column value remains unchanged.
This is due to the way that update method instantiate the ActiveRecord to update:
public void Update(Guid MyKey, ...)
{
MyItem item = new MyItem();
item.MarkOld();
item.IsLoaded = true;
...
item.Save(UserName);
}
By creating an empty MyItem instance (all fields are null) and setting a nullable field to null doesn' t allow the column to finish in the DirtyColumns collection (see ActiveHelper GetUpdateCommand).
Am I going to have write custom update functions for EVERY table in my database to resolve this?

Error in Linq: The text data type cannot be selected as DISTINCT because it is not comparable

I've a problem with LINQ. Basically a third party database that I need to connect to is using the now depreciated text field (I can't change this) and I need to execute a distinct clause in my linq on results that contain this field.
I don't want to do a ToList() before executing the Distinct() as that will result in thousands of records coming back from the database that I don't require and will annoy the client as they get charged for bandwidth usage. I only need the first 15 distinct records.
Anyway query is below:
var query = (from s in db.tSearches
join sc in db.tSearchIndexes on s.GUID equals sc.CPSGUID
join a in db.tAttributes on sc.AttributeGUID equals a.GUID
where s.Notes != null && a.Attribute == "Featured"
select new FeaturedVacancy
{
Id = s.GUID,
DateOpened = s.DateOpened,
Notes = s.Notes
});
return query.Distinct().OrderByDescending(x => x.DateOpened);
I know I can do a subquery to do the same thing as above (tSearches contains unique records) but I'd rather a more straightfoward solution if available as I need to change a number of similar queries throughout the code to get this working.
No answers on how to do this so I went with my first suggestion and retrieved the unique records first from tSearch then constructed a subquery with the non unique records and filtered the search results by this subquery. Answer below:
var query = (from s in db.tSearches
where s.DateClosed == null && s.ConfidentialNotes != null
orderby s.DateOpened descending
select new FeaturedVacancy
{
Id = s.GUID,
Notes = s.ConfidentialNotes
});
/* Now filter by our 'Featured' attribute */
var subQuery = from sc in db.tSearchIndexes
join a in db.tAttributes on sc.AttributeGUID equals a.GUID
where a.Attribute == "Featured"
select sc.CPSGUID;
query = query.Where(x => subQuery.Contains(x.Id));
return query;

Resources