SELECT partition_int, clustering_int, value_string
FROM test_ks1.test WHERE partition_int = ? AND clustering_int IN ?
Prepared select query with in clause throws the following exception:
java.lang.ClassCastException: class com.datastax.oss.driver.internal.core.type.PrimitiveType cannot be cast to class com.datastax.oss.driver.api.core.type.ListType
(com.datastax.oss.driver.internal.core.type.PrimitiveType and com.datastax.oss.driver.api.core.type.ListType are in unnamed module of loader 'app')
at com.datastax.oss.driver.internal.core.type.codec.registry.CachingCodecRegistry.inspectType(CachingCodecRegistry.java:343)
at com.datastax.oss.driver.internal.core.type.codec.registry.CachingCodecRegistry.codecFor(CachingCodecRegistry.java:256)
at com.datastax.oss.driver.internal.core.data.ValuesHelper.encodePreparedValues(ValuesHelper.java:112)
at com.datastax.oss.driver.internal.core.cql.DefaultPreparedStatement.bind(DefaultPreparedStatement.java:159)
Using datastax oss driver - version 4.5.1 and cosmosDB.
The query works with cassadra as docker and works in cqlsh with CosmosDB.
Queries used:
CREATE TABLE IF NOT EXISTS test_ks1.test (partition_int int, value_string text, clustering_int int, PRIMARY KEY ((partition_int),clustering_int))
Prepare the statement: INSERT INTO test_ks1.test (partition_int,clustering_int,value_string) values (?,?,?)
Insert values: 1,1,”a” | 1,2,”b”
Prepare the statement: SELECT partition_int, clustering_int, value_string FROM test_ks1.test WHERE partition_int = ? AND clustering_int IN ?
Execute with parameters 1,List.of(1,2)
The expected parameter is an integer and not list of integers
Sample code of the select prepared statement:
final CqlSessionBuilder sessionBuilder = CqlSession.builder()
.withConfigLoader(loadConfig(sessionConfig));
CqlSession session = sessionBuilder.build();
PreparedStatement statement = session.prepare(
"SELECT partition_int, clustering_int,"
+ "value_string FROM test_ks1.test WHERE partition_int = ? "
+ "AND clustering_int IN ?");
com.datastax.oss.driver.api.core.cql.ResultSet rs =
session.execute(statement.bind(1,List.of(1, 2)));
Is there a workaround to use prepared select queries with in clause?
Thanks.
My suggestion would be to use an work around like this:
//IDS you want to use;
List<Integer> list = Arrays.asList(1, 2);
// Multiply the number of question marks
String markers = StringUtils.repeat("?,", list.size()-1);
//Final query
final String query = "SELECT * FROM test_ks1.test where clustering_int in ("+markers+" ?)";
PreparedStatement prepared = session.prepare(query);
BoundStatement bound = prepared.bind(list.toArray()).setIdempotent(true);
List<Row> rows = session.execute(bound).all();
I have tried on my end and it works with me.
Now on your case you also have another parameter before the IN that you need to include in the parameter list but only after build the markers placeholder.
Related
I have a scenario where I am getting a SQL query and SQL arguments (to avoid SQL injection) as input.
And I am running that SQL using VoltDB's AdHoc stored procedure using below code.
private static final String voltdbServer = "localhost";
private static final int voltdbPort = 21212;
public ClientResponse runAdHoc(String sql, Object... sqlArgs) throws IOException, ProcCallException
{
ClientConfig clientConfig = new ClientConfig();
Client voltdbClient = ClientFactory.createClient(clientConfig);
voltdbClient.createConnection(voltdbServer, voltdbPort);
return voltdbClient.callProcedure("#AdHoc", sql, sqlArgs);
}
But I get an error org.voltdb.client.ProcCallException: SQL error while compiling query: Incorrect number of parameters passed: expected 2, passed 1
For runAdHoc("select * from table where column1 = ? and column2 = ?", "column1", "column2"), when there are two or more parameters.
And I get error org.voltdb.client.ProcCallException: Unable to execute adhoc sql statement(s): Array / Scalar parameter mismatch ([Ljava.lang.String; to java.lang.String)
For runAdHoc("select * from table where column1 = ?", "column1");, when there is only one parameter.
But I do not face this problem when I directly call voltdbClient.callProcedure("#AdHoc", "select * from table where column1 = ? and column2 = ?", "column1", "column2")
I think VoltDb is not able to treat sqlArgs as separate parameters instead, it is treating them as one array.
One way to solve this problem is parsing the SQL string myself and then passing it but I am posting this to know the efficient way to solve this problem.
Note:- Used SQL is just a test SQL
The #Adhoc system procedure is recognizing the array as one parameter. This kind of thing happens with #Adhoc because there is no planning of the procedure going on where one can explicitly state what each parameter is.
You have the right idea about parsing the sqlArgs array into the actual parameters to pass in separately. You could also concatenate these separate parameters into the SQL statement itself. That way, your adhoc statement will simply be:
voltdbClient.callProcedure("#AdHoc", sql)
Full disclosure: I work at VoltDB.
I posted the same question on VoltDB public slack channel and got one response which solved the problem which is as follows:
The short explanation is that your parameters to #Adhoc are being turned into [sql, sqlArgs] when they need to be [sql, sqlArg1, sqlArg2, …]. You’ll need to create a new array that is sqlArgs.length + 1, put sql at position 0, and copy sqlArgs into the new array starting at position 1. then pass that newly constructed array in the call to client.callProcedure("#AdHoc", newArray)
So I modified my runAdHoc method as below and it solved this problem
public ClientResponse runAdHoc(String sql, Object... sqlArgs) throws IOException, ProcCallException
{
ClientConfig clientConfig = new ClientConfig();
Client voltdbClient = ClientFactory.createClient(clientConfig);
voltdbClient.createConnection(voltdbServer, voltdbPort);
Object[] procArgs;
if (sqlArgs == null || sqlArgs.length == 0)
{
procArgs = new Object[1];
} else
{
procArgs = new Object[sqlArgs.length + 1];
System.arraycopy(sqlArgs, 0, procArgs, 1, sqlArgs.length);
}
procArgs[0] = sql;
return voltdbClient.callProcedure("#AdHoc", procArgs);
}
I have a requirement to get the where condition passed by user as program arguments. Based on the where condition i need to query the source data base.
I am using spark-sql.2.3.1
How to construct and pass/executive dynamically build query?
Sample query:
select ProductId, COUNT(*) AS ProductSaleCount
from productsale
where to_date(Date) >= "2015-12-17"
and to_date(Date) <= "2015-12-31"
group by ProductId
All you have to do in your scenario is create a query string which would go something like:
val query = "select ProductId, COUNT(*) AS ProductSaleCount from productsale where to_date(Date) >= "+ fromDate +" and to_date(Date) <= " + toDate + " group by ProductId"
the fromDate and toDate, you would get from your arguments, perhaps.
To use this, however is a different issue and it depends on your database
For hive you can simply register your spark session with enableHiveSupport
val spark = SparkSession.builder().appName("My App").enableHiveSupport().config("spark.sql.warehouse.dir", warehouseLocation).getOrCreate()
val data = spark.sqlContext.sql(query)
If the data is in a dataframe and you want to query that, you would have to create a view and then run your query on that
finalDataFrame.createOrReplaceTempView("productsale")
val data = spark.sqlContext.sql(query)
Hope this helps
I have a table with one timestamp column . When i try to execute a date filter using this timestamp column it doesn't give any results. Table structure and code segment is follows.
create table status_well
(
wid int,
data_time timestamp,
primary key (wid ,data_time)
)
SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
PreparedStatement statement = session.prepare("select from status_well where data_time>? and data_time<?");
BoundStatement boundStatement=new BoundStatement(statement);
statement.setDate("data_time", DATE_FORMAT.parse("2015-05-01"));
statement.setDate("data_time", DATE_FORMAT.parse("2015-05-10"));
Data is there for the above specified date range but no data returns . I tried with a string instead of DATE_FORMAT.parse("2015-05-01") but that gives an invalid type error .
Please advise me on this.
There are two placeholders for the data_time column, so you need to use index-based setters, or named placeholders:
PreparedStatement statement = session.prepare("select * from status_well "
+ "where wid = :wid "
+ "and data_time > :min and data_time < :max");
BoundStatement boundStatement = new BoundStatement(statement)
.setInt("wid", 1)
.setDate("min", DATE_FORMAT.parse("2015-05-01"))
.setDate("max", DATE_FORMAT.parse("2015-05-10"));
(also added a restriction on wid to get a valid CQL query, as was mentioned in the comments)
I have a table created like this:
CREATE TABLE messages (
stream int,
sequence int,
timestamp bigint,
message blob,
PRIMARY KEY (stream, sequence)
) WITH gc_grace_seconds = 0;
Running the following query in CQLSH works perfectly fine:
select * from messages where stream = 1 and sequence >= 1 and sequence <= 100;
However, when I try to run the same via Java driver I get the following exception:
com.datastax.driver.core.exceptions.InvalidQueryException: String didn't validate.
at com.datastax.driver.core.exceptions.InvalidQueryException.copy(InvalidQueryException.java:35)
at com.datastax.driver.core.ResultSetFuture.extractCauseFromExecutionException(ResultSetFuture.java:271)
at com.datastax.driver.core.ResultSetFuture.getUninterruptibly(ResultSetFuture.java:187)
at com.datastax.driver.core.Session.execute(Session.java:126)
at com.datastax.driver.core.Session.execute(Session.java:100)
I'm using parameterized querying API:
public final String FETCH_CQL = "select stream, sequence, timestamp, message "
+ "from messages where stream = ? and sequence >= ? and sequence <= ?";
session.execute(FETCH_CQL, Integer.parseInt(stream), Integer.parseInt(fromSequence), Integer.parseInt(toSequence));
What gives? Overall setup works, as I have another query working on a different table.
Thanks!
Have you tried adding ; to the end of your query:
public final String FETCH_CQL = "select stream, sequence, timestamp, message "
+ "from messages where stream = ? and sequence >= ? and sequence <= ?**;**";
Another option is to use prepared statements:
PreparedStatement FETCH_PS = session.prepare(FETCH_CQL);
BoundStatement boundStatement = FETCH_PS.bind(Integer.parseInt(stream), Integer.parseInt(fromSequence), Integer.parseInt(toSequence));
session.execute(boundStatement);
Define stream and sequence fields as type UUID
Can I do something like this on JPQL?
SELECT NEW com.MyDTO(p.a, p.b, q.c, q.d)
FROM
(SELECT r.* FROM MyDTO1 r ) p
LEFT OUTER JOIN
(SELECT s.* FROM MyDTO2 s ) q
ON p.x = q.y
or similar?
(Above query has mixed with native and JPQL, so don't misunderstand)
I'm having a problem with this part I think.
FROM
(SELECT r.* FROM MyDTO1 r ) p
When I'm trying to execute I'm getting this error.
Exception Description: Syntax error parsing the query [.....], unexpected token [(]
Thank you!
No, you can't. Quote from the documentation:
Note that HQL subqueries can occur only in the select or where
clauses.
Yes you can!
You have to use native queries. Here is an example:
emf = Persistence.createEntityManagerFactory("TEST")
EntityManager em = emf.createEntityManager();
String queryString = "SELECT ID FROM ( SELECT * FROM ADDRESS WHERE ID < 0)";
Query query = em.createNativeQuery(queryString);
List<BigDecimal> result = query.getResultList();