Insert data from a JsonArray using updateWithParms in vertx - sql

Is is possible to use updatewithparms() in vertx postgres client to insert data to the using an json array. I tried the following with out any success.
String INSERT_QUERY="INSERT INTO testDb (rid , aid , created_at, expiry_time, strings , strings) VALUES"
private JsonArray preparedParameters(){
//JsonArray params = new JsonArray();
DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return new JsonArray()
.add(device.getRecordId().toString())
.add( device.getAccountId())
.add(dateTimeFormat.format(device.getCreatedAt()))
.add(dateTimeFormat.format(device.getExpiresAt()))
.add( device.getEnrollmentId())
.add(device.getHashedEnrollmentId());
}
Then I call functions as follows.
try (SQLConnection connection = connectionResult.result()) {
connection.updateWithParams(INSERT_QUERY,preparedParameters() ,queryRes -> {
if (queryRes.succeeded()) {
booleanFuture.complete(true);
} else {
booleanFuture.complete(false);
}
});
} catch (Exception e) {
booleanFuture.complete(false);
}
I receive the following error.
The query contains 0 parameters but you gave it 6 ("sda","sda",2018-01-22 20:23:26,2018-02-21 20:23:26,"sda","sda")
com.github.mauricio.async.db.exceptions.InsufficientParametersException: The query contains 0 parameters but you gave it 6 ("sda","sda",2018-01-22 20:23:26,2018-02-21 20:23:26,"sda","sda")

Your query is not valid, it misses the parameters.
String INSERT_QUERY="INSERT INTO testDb (rid , aid , created_at, expiry_time, strings , strings) VALUES (?,?,?,?,?,?)"
See the Prepared Statement Updates section of the Vert.x SQL doc.

Related

Select Count(*) Query using Dapper in .Net Core API returns incorrect value

I'm trying to do a select count query in Sql Server using Dapper. The expected response should be 0 when a profile does not exist. When I do the query in SSMS it returns correctly, but in the API using Dapper it returns 1. Any idea why this is happening?
public IActionResult GetProfileCount(string profileId)
{
int profileCount = 0;
using (IDbConnection db = new SqlConnection(connectionString))
{
try
{
profileCount = db.Query($"select count(*) from Profile where Id='{profileId}'").Count();
}
catch(Exception ex)
{
Console.WriteLine($"Error retrieving count for ProfileId: {profileId}", ex.Message);
}
}
return Ok(profileCount);
}
I see you added your own answer but could I recommend not doing it that way. When you do
profileCount = db.Query($"select * from Profile where Id='{profileId}'").Count();
What you are actually doing is selecting every field from the database, pulling it into your C# application, and then counting how many results you got back. Then you are binning all that data you got back, very inefficient!
Change it to this :
profileCount = db.QueryFirst<int>($"select count(*) from Profile where Id = #profileId", new { profileId })");
Instead you are selecting an "int" from the result set, which just so happens to be your count(*). Perfect!
More on querying in Dapper here : https://dotnetcoretutorials.com/2019/08/05/dapper-in-net-core-part-2-dapper-query-basics/
Also notice that (similar to the other answer), I am using parameterized queries. I also heavily recommend this as it protects you from SQL Injection. Your initial example is very vulnerable!
You can read a little more about SQL Injection in C#/MSSQL here https://dotnetcoretutorials.com/2017/10/11/owasp-top-10-asp-net-core-sql-injection/ But just know that Dapper protects you from it as long as you use the inbuilt helpers to add parameters to your queries.
Another option is use the method ExecuteScalar for "select count" queries:
profileCount = db.ExecuteScalar<int>("select count(*) from Profile where Id=#profileId", new { profileId });
Ref.: https://www.learndapper.com/selecting-scalar-values
Try and change your query to the following:
db.Query($"select count(*) from Profile where Id = #ProfileId", new { ProfileId = profileId }).Count()
I figured it out. The .Count() is counting the rows of the result, which is going to be 1 because the result is one row displaying the number 0. I switched my code to this and it works now.
public IActionResult GetProfileCount(string profileId)
{
int profileCount = 0;
using (IDbConnection db = new SqlConnection(connectionString))
{
try
{
profileCount = db.Query($"select * from Profile where Id='{profileId}'").Count();
}
catch(Exception ex)
{
Console.WriteLine($"Error retrieving count for ProfileId: {profileId}", ex.Message);
}
}
return Ok(profileCount);
}

How to insert multiple integer parameters into query?

Website user can enter search criteria to query orders. User, States, Status, OrderID, etc.
Website communicates with API. Query parameters are in the header, so I assume they come in as strings. API communicates with Access via Dapper.
For some criteria, they can send multiple values. So I want to use an "IN" clause.
where UserID in (150, 3303, 16547)
Dapper handles this nicely.
connection.Query<int>("select * from table where Id in #Ids", new { Ids = new int[] { 1, 2, 3 } });
This works in MS-Access
SELECT top 100 * from Orders where UserID in (150, 30330)
But that only works when the values are ints. String and Strings both give "Data type mismatch in criteria expression" in Access.
SELECT top 100 * from Orders where UserID in ("150", "30330") // two strings
SELECT top 100 * from Orders where UserID in ("150, 30330") // single string
It may be a coincidence, but all the examples I see are integers. Access throws an error on strings if you don't specify the size. Using DynamicParameters makes it easy to specify the size.
But when the field is an int, my dapper code gives the same error (Data type mismatch in criteria expression):
var paramlist = new DynamicParameters();
if ((!string.IsNullOrWhiteSpace(userId)) && userId != "0") {
paramlist.Add("userId", userId, DbType.String, ParameterDirection.Input, 50);
sbWhere.AppendFormat("AND CustFID in (?) ", paramIndex++);
}
So I assume the issue is that I'm telling it that the parameter is a string.
But if I make the parameter an int, then it won't take the string with multiple values. Conversely, if I include the () in the string, it complains about the parens being missing from the 'in' clause.
I tried splitting the string of numbers into an array and/or list.
if ((!string.IsNullOrWhiteSpace(userId)) && userId != "0") {
var userIds = userId.Split(','); //.ToList(); fails, can't map to native type
paramlist.Add("userId", userIds, DbType.String, ParameterDirection.Input, 1000);
if (userIds.Length > 1) {
sbWhere.AppendFormat("AND CustFID in #userId ", paramIndex++);
} else {
sbWhere.AppendFormat("AND CustFID = #userId ", paramIndex++);
}
}
and it gives ": No mapping exists from object type System.String[] to a known managed provider native type." whether I say the parameters are int32 or string.
UPDATE:
There may be multiple search criteria, so I'm using DynamicParameters.
Here is my attempt at implementing Palle Due's idea.
if ((!string.IsNullOrWhiteSpace(userId)) && userId != "0") {
// var userIds = userId.Split(',').Select(i => Int32.Parse(i)).ToList();// fails, can't map to native type
IEnumerable<int> userIds = userId.Split(',').Select<string, int>(int.Parse);
paramlist.Add("userId", userIds, DbType.Int32, ParameterDirection.Input);
if (userIds.Count() > 1) {
sbWhere.AppendFormat("AND CustFID in #userId ", paramIndex++);
} else {
sbWhere.AppendFormat("AND CustFID = #userId ", paramIndex++);
}
}
using (IDbConnection conn = Connection) {
string sQuery = string.Format("SELECT {0} FROM vwweb_Orders {1}", columns, where);
conn.Open();
var result = await conn.QueryAsync<Order>(sQuery, paramlist);
return result.ToList();
}
throws
Message: System.AggregateException : One or more errors occurred. (Failed to convert parameter value from a SelectArrayIterator`2 to a Int32.)
----> System.InvalidCastException : Failed to convert parameter value from a SelectArrayIterator`2 to a Int32.
----> System.InvalidCastException : Object must implement IConvertible.
The github page #Dai links to specifies that the Dapper list support only works with IEnumerable<int>.
But as I understand it your UserID is an int, so I don't get why you try to enter a string. You need to get the string the user has input and convert it to IEnumerable<int>. Do something like this:
IEnumerable<int> userIDs = (userId?? "").Split(',').Select<string, int>(int.Parse);
var result = connection.Query<int>("SELECT TOP 100 * FROM Orders WHERE UserID IN #Ids", new { Ids = userIDs });
You might want to apply some input checking to that, and you might also want to reconsider using Access as the "database" for a website. It's not what it was meant for.
I give up. Dapper should be able to handle this, but it's a newer feature, so...
I just built the IN clause myself.
if (userIds.Count() > 1) {
sbWhere.AppendFormat("AND CustFID in ( ");
int paramCnt = 0;
foreach (int id in userIds) {
sbWhere.AppendFormat("?, "); // Access doesn't mind a trailing ,
paramlist.Add("userId" + paramCnt.ToString(), id, DbType.Int32, ParameterDirection.Input);
paramCnt++;
}
sbWhere.AppendFormat(") ");
} else {
sbWhere.AppendFormat("AND CustFID = ? ");
paramlist.Add("userId", userIds.ToArray<int>()[0], DbType.Int32, ParameterDirection.Input);
}

SQLite Error in statement insert with question mark

Hello I am writing a query to insert values in DB SQLite.
Firstly, I read from my records and searched for two values, for example, then I created a new table and inserted new values in.
My code snippet follows:
try
{
Class.forName("org.sqlite.JDBC");
Connection con=DriverManager.getConnection("jdbc:sqlite:tests.db");
con.setAutoCommit(false);
Statement st=con.createStatement();
st.executeUpdate("delete msearch");
ResultSet res=st.executeQuery("select * from newmobile_details");
Boolean rec=res.next();
if(!rec)
{
JOptionPane.showMessageDialog(null,"لايوجد سجلات");
}
else
{
do
{
String mid=res.getString(1);
String model=res.getString(2);
String name=res.getString(3);
int price=res.getInt(4);
String pcolor=res.getString(5);
String imei=res.getString(6);
java.sql.Date date=res.getDate(7);
String access=res.getString(8);
if(mname.equalsIgnoreCase(name))
{
PreparedStatement prp=con.prepareStatement("insert into msearch values(?,?,?,?,?,?,?,?)");
prp.setString(1,mid);
prp.setString(2,model);
prp.setString(3,name);
prp.setInt(4,price);
prp.setString(5,pcolor);
prp.setString(6,imei);
prp.setDate(7,date);
prp.setString(8,access);
prp.executeUpdate();
System.out.println("iam inside2");
rows++;
b=1;
jTextField2.setText("");
}
}while(res.next());
if(b==0)
{
JOptionPane.showMessageDialog(null,"لم يتم العثور على الموبايل ");
jTextField2.setText("");
}
}
con.commit();
con.close();
}
catch(Exception e)
{
JOptionPane.showMessageDialog(null,"The error is1:" +e);
}
I get only exception as below:
The error is1 :sql error or missing database in msearch syntax error
Larry makes a good point and looking at your code more carefully I believe the problem is with the statement
st.executeUpdate("delete msearch");
It seems to be saying you should be qualifying msearch with a database name.
This is the syntax for an insert statement
INSERT INTO TABLE_NAME (c1, c2, c3,...cN)
VALUES (v1, v2, v3,...vN);
You give a list of column names and a list of values.
You don't have the list of column names.
This can't work.

jdbcTemplate.query works fine when passing few parameters and doesn't work for others

I have a select Oracle SQL that I am hitting using jdbcTemplate.query method. This returns a bean of the values from the table. I am passing a dynamic value to the query that will be used in the WHERE clause. However, the SQL values for few values that i am passing. But when I pass the value as NA it won't work. Any suggestions on this or help me with what am i missing?
private static final String regionSearchSql = "SELECT PRFLID, PRFLNM, RGN_CD FROM %PREFIX%MER_PRFL WHERE RGN_CD = ?";
public List<SearchProfileBean> regionSearchProfile(SearchProfileRequest searchProfileRequest) throws DatabaseQueryException {
try {
return jdbcTemplate.query((QueryUtility.getQueryWithPrefix(regionSearchSql,prefix)), new SearchProfileRowMapper(), searchProfileRequest.getRegionName());
} catch (Exception e) {
throw new DatabaseQueryException(QueryUtility.getQueryWithPrefix(regionSearchSql, prefix), e);
}
}
If i pass 'EMEA', 'LAC', 'JAPA' in searchProfileRequest.getRegionName() - the SQL returns perfect results. But if I pass 'NA' in searchProfileRequest.getRegionName(), it gives empty results. But there are rows in the table for NA.

Apache Dbutils changing column name in update Sql

I am having a strange problem with Dbutils , I am trying to run a parameterized update sql, I am supplying correct number of arguments , but dbutils is modifying the timestamp column name by changing the name of modifying it
when timestamp columnname is one alphabet
java.sql.SQLException: Wrong number of parameters: expected 4, was
given 5 Query: UPDATE WEATHER_2 SET WEATHER=? ,
O=TO_TIMESTAMP(?,'YYYY-MM-DD HH24:MI:SS.FF') , HUMIDITY=? , TEMP=?
WHERE ID=? Parameters: [804, 2015-06-05 17:21:05.809, 16.0, 25.15,
1347927]
when timestamp columnname is normal..it will ommit the second alphabet
java.sql.SQLException: ORA-00904: "OSTIME": invalid identifier
Query: UPDATE WEATHER_2 SET WEATHER=? , OBSTIME=TO_TIMESTAMP(?,'YYYY-MM-DD HH24:MI:SS.FF') , HUMIDITY=? ,
TEMP=? WHERE ID=? Parameters: [804, 2015-06-05 17:27:46.139, 16.0,
25.15, 1347927]
could this be a database thing? Also this is only happening with column whose type is Date or Timestamp.
I had a similar issue. I think it is a bug in the Oracle JDBC 7 Driver (ojdbc7.jar). The bug could be in the PreparedStatement.getParameterMetaData method.
This method is used internally by the Apache DBUtils. So it would not be a bug of DBUtils, but a bug from Oracle JDBC driver distributed with Oracle 12c.
Same Query will probably work fine if you use the Oracle 11g ojdbc6.jar driver. It at least worked for me.
If you want to see how the Query is wrongly processed internally by the Oracle ojdbc7.jar driver, you can use the main method included in the oracle.jdbc.driver.OracleParameterMetaDataParser class. Try this:
java -classpath ojdbc7.jar
oracle.jdbc.driver.OracleParameterMetaDataParser "YOUR SQL HERE"
e.g.
java -classpath ojdbc7.jar
oracle.jdbc.driver.OracleParameterMetaDataParser "UPDATE PERSON SET
LASTNAME=?, FIRSTNAME=? WHERE PERSONID=?"
The output is your SQL Sentence parsed and converted to a SQL Query that is used internally by the driver to identify the parameter datatypes:
SQL:UPDATE PERSON SET LASTNAME=:1 , FIRSTNAME=:2 WHERE PERSONID=:3
SqlKind:UPDATE, Parameter Count=3 Parameter SQL: SELECT LASTNAME, F,
PERSONID FROM PERSON
But as you can see in the sample, the FIRSTNAME is wrongly parsed just as "F".
Using one of the Queries you put in your question, the result is that one of the parameters just disappear... so the parser says "5" params but the internal Query used to get the datatypes has indeed only "4" (HUMIDITY has gone from the SELECT).
java -classpath ojdbc7.jar
oracle.jdbc.driver.OracleParameterMetaDataParser "UPDATE WEATHER_2 SET WEATHER=? , OBSTIME=TO_TIMESTAMP(?,'YYYY-MM-DD HH24:MI:SS.FF') , HUMIDITY=? , TEMP=? WHERE ID=?"
output:
SQL:UPDATE WEATHER_2 SET WEATHER=:1 , OBSTIME=TO_TIMESTAMP(:2
,'YYYY-MM-DD HH24:MI:SS.FF') , HUMIDITY=:3 , TEMP=:4 WHERE ID=:5
SqlKind:UPDATE, Parameter Count=5
Parameter SQL: SELECT WEATHER, OBSTIME, TEMP, ID FROM WEATHER_2
How to fixit? No idea, but as I said above, using the Oracle 11g ojdbc6.jar driver, same query works (even connecting with an Oracle 12c database...).
The behaviour is pretty random. It looks like it depends on the first letter of the column used in the UPDATE. If it begins with F and H always fails, but I do not know if there is any other condition.
oracle 12.1.0.1.0 jdbc oracle.jdbc.driver.OracleParameterMetaDataParser suck !
on my test:
oracle.jdbc.driver.OracleParameterMetaDataParser.main(new String[]{"update test set ORDX=?,A123=?,FABCDEFG=? where A2C=?"})
==>
SQL:update test set ORDX=:1 ,A123=:2 ,FABCDEFG=:3 where A2C=:4
SqlKind:UPDATE, Parameter Count=4
Parameter SQL: SELECT OX, A23, F, AC FROM test
field start with "O" will trim 1-2 char
field start with "F" will trim all
field start with "A" will trim 1 char
on oralce 12.1.0.2.0 still have on issue:
field start with "F" will trim all
http://www.oracle.com/technetwork/database/features/jdbc/default-2280470.html
I encounter the same problem, using DBUtils and from ojdbc6 upgrade to ojdbc7. Then I am aware of this is a bug on parameter, so I fill it by myself. As this:
update():
Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql.toUpperCase());
this.fillStatement(ps, param);
int rs = ps.executeUpdate();
ps.close();
conn.close();
fillStatement():
private void fillStatement(PreparedStatement stmt, Object... params) throws SQLException {
int i = 1;
for (Object o: params){
fill(stmt, i, o);
i ++;
}
}
private void fill(PreparedStatement stmt, int index, Object param) throws SQLException {
if (param == null) {
stmt.setObject(index, null);
} else if (param instanceof String) {
stmt.setString(index, (String) param);
} else if (param instanceof Boolean) {
stmt.setBoolean(index, (Boolean) param);
} else if (param instanceof Integer) {
stmt.setInt(index, (Integer) param);
} else if (param instanceof Long) {
stmt.setLong(index, (Long) param);
} else if (param instanceof Double) {
stmt.setDouble(index, (Double) param);
} else if (param instanceof Float) {
stmt.setFloat(index, (Float) param);
} else if (param instanceof Short) {
stmt.setShort(index, (Short) param);
} else if (param instanceof Clob) {
stmt.setClob(index, (Clob) param);
} else if (param instanceof Blob) {
stmt.setBlob(index, (Blob) param);
} else if (param instanceof java.sql.Timestamp) {
stmt.setTimestamp(index, (java.sql.Timestamp) param);
} else if (param instanceof BigDecimal) {
stmt.setBigDecimal(index, (BigDecimal) param);
}else if (param instanceof java.sql.Time) {
stmt.setTime(index, (java.sql.Time) param);
} else if (param instanceof java.sql.Date) {
stmt.setDate(index, (java.sql.Date) param);
} else if (param instanceof Date) {
stmt.setDate(index, new java.sql.Date(((Date) param).getTime()));
} else {
stmt.setObject(index, param);
}
}