First crack at using SQLite+Java and I'm recieving an error when I attempt to execute a simple simple query.
Error:
not implemented by SQLite JDBC driver
Query:
String sql =
"select Asset, Qty*Price+Fees as Cost \n" +
"from Transactions t \n" +
" inner join TransactionItems i on t.Id = i.TransactionId \n" +
"where TransDate <= ? \n";
try (PreparedStatement stmt = cnn.prepareStatement(sql)) {
java.sql.Date dte = new java.sql.Date(SumDate().getTimeInMillis());
stmt.setDate(1, dte);
try(ResultSet rs = stmt.executeQuery(sql)) {
while(rs.next()) {
PortfolioSummaryItem item = new PortfolioSummaryItem(PortfolioSummary.this);
item.Load(rs);
items.put(item.asset,item);
}
rs.close();
}
stmt.close();
This was a simple cut/paste style error. When using prepared statements, you shouldn't then pass the SQL into the executeQuery.
Change:
try(ResultSet rs = stmt.executeQuery(sql)){
To:
try(ResultSet rs = stmt.executeQuery()){
This was overriding the preparedStatement.
What it was complaining about was executing a query with a '?' in it since it wasn't the prepared query.
Check the jdbc driver you have in the libs folder.
It looks like it has not implemented the methods you have called.
Try downloading the driver from here:
https://bitbucket.org/xerial/sqlite-jdbc/downloads
Related
I have a Java-code:
String searchPerson = "select * from persons where surname like ? and name like ?";
//connect to DB
PreparedStatement statement = connect.prepareStatement(searchPerson);
statement.setString(1,"%"+ surname + "%");
statement.setString(2, "%" + name + "%");
ResultSet resultPerson = statement.executeQuery(searchPerson);
//..code
Then I have SQLException:
you have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?'
You should execute the PrepareStatement with no parameters as follows:
statement.executeQuery()
Calling executeQuery with a String parameter will execute the provided query as is (without the bound parameters).
ResultSet resultPerson = statement.executeQuery(searchPerson);
should be
ResultSet resultPerson = statement.executeQuery();
Try with statement.setString(1,"'%"+ surname + "%'");
The query inside MySQL is working:
DELETE FROM f9.yoo
WHERE account_tags = '#8GGGJPUR9'
I can delete data inside MySQL, but the problem is whenever I try to remove the account_tags from my Java application, it throws an error:
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELETE FROM f9.yoo
WHERE account_tags = '#8GGGJPUR9'' at line 2
Here's my Java SQL query:
Statement statement = dbConnection.createStatement();
String sql = "SELECT * FROM "+databaseName+"."+tableName+";\n" +
"DELETE FROM "+databaseName+"."+tableName+"\n" +
"WHERE account_tags = '"+AccountTag+"';";
statement.executeQuery(sql);
The error isn't giving me much to work with, so I really have no idea what is wrong with the program.
Did you add the allowMultiQueries=true
If not then you can add that while you sending the connecting request to your database. So you need to append the allowMultiQueries=true in your to database URL.
Like this:
String dbUrl = "jdbc:mysql:///test?allowMultiQueries=true";
String sql = "DELETE FROM "+databaseName+"."+tableName+"\n" +
"WHERE account_tags = ?";
try (PreparedStatement statement = dbConnection.prepareStatement(sq)) {
statement.setString(1, AccountTag);
int updateCount = statement.executeUpdate();
System.out.printf("%s: %d records deleted.%n", tableName, updateCount);
}
The only thing used is the DELETE, for which one should use executeUpdate.
One definitely should use a PreparedStatement as many code checkers will give alarms otherwise. It escapes things like ', handles types of the arguments, and possible conversions, and especially is a security feature against SQL injection.
The System.out usage is bad style, better would be using a logger.
try-with-resources automatically closes the PreparedStatement even with a raised exception or break/return.
When doing both database operations, it seems better to use two (prepared) statements, as the first returns a ResultSet.
So:
String sql = SELECT * FROM "+databaseName+"."+tableName + "\n" +
"WHERE account_tags = ?";
try (PreparedStatement statement = dbConnection.prepareStatement(sq)) {
statement.setString(1, AccountTag);
try (ResultSet rs = statement.executeQuery()) {
...
}
}
Better to separate statements with an If condition :
String sql1="SELECT * FROM "+databaseName+"."+tableName;
String sql2="DELETE FROM "+databaseName+"."+tableName+" "+
"WHERE account_tags = '"+AccountTag+"';
statement.executeQuery(sql1);
statement.executeUpdate(sql2);
A security scan made by AppScan source flags that the input has to be validated (Validation.Required) on the line uprs.updateString in the code below:
PreparedStatement statement =
conn.prepareStatement (query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
...
ResultSet uprs = statement.executeQuery ();
...
// Update DB ColumnA with input coming from client
uprs.updateString ('ColumnA', unvalidatedUserInput);
...
// Updates the underlying database
uprs.updateRow();
I assume that the intention behind this is to avoid SQL injection attacks, but I'm not sure whether that is possible in that scenario.
Questions: Are SQL Injection attacks possible through these JDBC methods? How does JDBC implements this under the scenes? Would this be another false positive reported by AppScan?
I'm not sure about bluemix-app-scan, but I'm providing my explanation here. (This is my assumption based on the below tests and code pasted)
I ran a test code to check this (in H2 DB)
value of testName String : (select 'sqlInjection' from dual)
Using createStatement (Not-Safe):
String query = "update TEST_TABLE set TEST_CHAR = " + testName + " where ID = 1";
Statement statement = connection.createStatement();
statement.executeUpdate(query);
Output: TEST_CHAR in DB was sqlInjection.
Using ResultSet of createStatement (Safe in H2 DB):
String query = "select * from TEST_TABLE where ID = 1";
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet executeQuery = statement.executeQuery(query);
executeQuery.next();
executeQuery.updateString("TEST_CHAR", testName);
executeQuery.updateRow();
Output: Surprisingly TEST_CHAR in DB was (select 'sqlInjection' from dual).
Using PreparedStatement (Safe):
String query = "update TEST_TABLE set TEST_CHAR = ? where ID = 1";
PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, testName);
statement.executeUpdate();
Output: Expected - TEST_CHAR in DB was (select 'sqlInjection' from dual).
Using ResultSet of prepareStatement (Safe in H2 DB):
String query = "select * from TEST_TABLE where ID = 1";
PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet uprs = statement.executeQuery();
uprs.next();
uprs.updateString("TEST_CHAR", testName);
uprs.updateRow();
Output: Expected - TEST_CHAR in DB was (select 'sqlInjection' from dual).
Back to Questions:
Are SQL Injection attacks possible through these JDBC methods?
Maybe. It depends on the database driver that you're using.
How? :
The reason SQL Injection failed in my result set update was because H2 database internally uses PreparedStatement to update the row when ResultSet.updateRow() is invoked.
public void updateRow(Value[] current, Value[] updateRow) throws SQLException {
StatementBuilder buff = new StatementBuilder("UPDATE ");
...
buff.append(" SET ");
appendColumnList(buff, true);
...
appendKeyCondition(buff);
PreparedStatement prep = conn.prepareStatement(buff.toString());
...
for (int i = 0; i < columnCount; i++) {
...
v.set(prep, j++);
}
setKey(prep, j, current);
int count = prep.executeUpdate();
...
}
I'm not sure if all DB drivers in java implemented updateRow() method using preparedStatement or not. However it's clear that this is left to the driver and if bluemix is suggesting you to add a validation here, I suggest you follow that :)
How does JDBC implements this under the scenes?
Well, as shown above this is driver specific. However there is a good explanation on how PreparedStatement handles it over here.
Would this be another false positive reported by AppScan?
I don't think this is false positive (but in cases like H2 DB it is) but you'll never know if all database drivers implemented this securely.
Edit -
Even PostgreSQL and MySQL use PreparedStatement to handle this.
public synchronized void updateRow() throws SQLException
{
...
updateStatement = ((java.sql.Connection) connection).prepareStatement(updateSQL.toString());
...
updateStatement.executeUpdate();
...
}
I have a SQL code in a java code which looks like this :
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
beforeExerTestDTO dto = new beforeExerTestDTO();
StringBuffer sql = new StringBuffer();
sql.append(" select * ");
sql.append(" from n_before_exer ");
sql.append(" where id=?");
sql.append(" and reg_date = (select max(reg_date) from n_before_exer where id=?)");
try {
con = pool.getLocalConnection();
pstmt = con.prepareStatement(sql.toString());
pstmt.setString(1, id);
pstmt.setString(2, id);
System.out.println("여기까진 살까??");
rs = pstmt.executeQuery();
/......
...... some code /
}catch(SQLException e){
System.out.println("read : " + e);
System.out.println("read : " + sql);
}catch(Exception e){
System.out.println("read : " + e.getStackTrace().toString());
}finally{
DBClose.close(con, pstmt, rs);
}
return dto;
}
When the file gets executed it forms a statement like this in console:
select * from n_before_exer where id=? and reg_date = (select max(reg_date) from n_before_exer where id=?)
and throws a
java.sql.SQLEXCEPTION
What I tried :
I ran the same in Mysql Workbench query :
and got the following error:
Error Code: 1064. You have an error in your SQL syntax; check the
manual that corresponds to your MySQL server version for the right
syntax to use near '? and reg_date = (select max(reg_date) from
n_before_exer where id=?)' at line 1
A bit of research on the topic shows :
This way is not a preferred way as it can lead to injection attacks
And was advised to use a placeholder for a parameter
It seems a bit complex for me, if anyone can help me construct this statement in the right preferred way please
Thanks
You should be using a prepared statement:
Connection con; // get a connection
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, someInt);
ps.setInt(2, someOtherInt);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
// process each record
}
Your statement seem correct in syntax. Are you have encoding issue on you java file?
pstmt.setString(1, id);
I guess the problem is that the type of id is not string ,you could use this to have a try:
Is there a way to retrieve the DISTRIBUTE clause from a Netezza table using JDBC MetaData?
After some searching, I found a temporary work around based on the link below:
http://pic.dhe.ibm.com/infocenter/ntz/v7r0m3/topic/com.ibm.nz.adm.doc/r_sysadm_user_views.html
The code below does the job, however not through DatabaseMetaData
String SQLString = "SELECT ATTNAME FROM _V_TABLE_DIST_MAP\n";
SQLString += "WHERE TABLENAME = '" + table + "'\n";
SQLString += "ORDER BY DISTSEQNO";
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(SQLString);
while( rs.next() ) {
System.out.println(rs.getString(1));
}
rs.close();