I have a search table where user will be able to filter results with a filter of the type:
Field [Name], Value [John], Remove
Rule
Field [Surname], Value [Blake],
Remove Rule
Field [Has Children], Value [Yes],
Remove Rule
Add Rule
So the user will be able to set an arbitrary set of filters, which will result essentially in a completely dynamic WHERE clause. In the future I will also have to implement more complicated logical expressions, like
Where (name=John OR name=Nick) AND (surname=Blake OR surname=Bourne),
Of all 10 fields the user may or may not filter by, I don't know how many and which filters the user will set. So, I cannot use a prepared statement (which assumes that at least we know the fields in the WHERE clause). This is why prepared statements are unfortunately out of the question, I have to do it with plain old, generated SQL.
What measures can I take to protect the application from SQL Injection (REGEX-wise or any other way)?
Java, untested.
List<String> clauses = new ArrayList<String>();
List<String> binds = new ArrayList<String>();
if (request.name != null) {
binds.add(request.name);
clauses.add("NAME = ?");
}
if (request.city != null) {
binds.add(request.city);
clauses.add("CITY = ?");
}
...
String whereClause = "";
for(String clause : clauses) {
if (whereClause.length() > 0) {
whereClause = whereClause + " AND ";
}
whereClause = whereClause + clause;
}
String sql = "SELECT * FROM table WHERE " + whereClause;
PreparedStatement ps = con.prepareStatment(sql);
int col = 1;
for(String bind : binds) {
ps.setString(col++, bind);
}
ResultSet rs = ps.executeQuery();
If you add arguments to prepared statements they will automatically be escaped.
conn = pool.getConnection( );
String selectStatement = "SELECT * FROM User WHERE userId = ? ";
PreparedStatement prepStmt = con.prepareStatement(selectStatement);
prepStmt.setString(1, userId);
ResultSet rs = prepStmt.executeQuery();
SQL Server: Dynamic where-clause
Build the where clause dynamically, but do it using parameter names.
Related
I need to write an update function where its content is different based on what parameters are passed, e.g. if I have updateBook(int id, String title, String author, int pages), I have to do something like:
String sql;
if((!title.equals("null"))&&(!author.equals("null"))&&(pages>0)))
sql = "UPDATE book SET title='"+title+"', author='"+author+"', pages="+pages;
else if(((!title.equals("null"))&&(!author.equals("null")))
sql = "UPDATE book SET title='"+title+"', author='"+author+"'";
else if(((!title.equals("null"))&&(pages>0)))
sql = "UPDATE book SET title='"+title+"', pages="+pages;
... //and so on
sql = sql + " WHERE bookid="+id+";";
The more fields I have in my table, the more checks I have to do, which is uncomfortable, and requires me to write a lot of code.
Also, doing something like:
sql = "UPDATE book SET ";
if(!title.equals("null"))
sql = sql +"title='"+title+"',";
if(!author.equals("null"))
sql = sql+"author='"+author+"',";
if(pages>0)
sql = sql+"pages="+pages";
sql = sql + ";";
can't work since the unwanted commas cause statement errors.
You can see as well that if I have something like 6, 7, 8 etc field the checks start to get too many, and I can't also do more separated update statements as if something goes wrong I would need to rollback any query that has been done in that function.
Is there any way round to get a custom update statement having to write few code?
Firstly, use a PreparedStatement.
I would do it something like the following.
List<Object> params = new ArrayList<>();
StringBuilder sql = new StringBuilder();
if(!title.equals("null")) {
sql.append("title = ?");
params.add(title);
}
if(!author.equals("null")) {
if (sql.length() > 0) {
sql.append(", ");
}
sql.append("author = ?");
params.add(author);
}
if(pages>0) {
if (sql.length() > 0) {
sql.append(", ");
}
sql.append("pages = ?");
params.add(pages);
}
if (sql.length() > 0) {
sql.insert(0, "UPDATE book SET ");
sql.append(" WHERE bookid=?");
java.sql.Connection conn = // however you obtain it
java.sql.PreparedStatement ps = conn.prepareStatement(sql.toString());
for (int i = 0; i < params.size(); i++) {
ps.setObject(i + 1, params.get(i));
}
ps.executeUpdate();
}
Trying to write condition where ind is null in the select query in java using jdbc oracle driver.
code :
Done all the DB connectivity
info.add("CN");
info.add("NULL");
Tried:
ResultSet rs1 = st.executeQuery("select COUNT(*) from TABLENAME where A='" + info.get(i) + " and ind is'" +info.get(i+1) + " '");
Note: using oracle driver JDBC API.
Taking the null value from array list.but it does not fetch proper values from DB.
code:
Done all the DB connectivity
info.add("CN");
info.add("NULL");
ResultSet rs1 = st.executeQuery("select COUNT(*) from TABLENAME where A='" + info.get(i) + " and ind IS '" +info.get(i+1) + " '");
I expect the output like count(no of rows):
BAsic sql query if used in DB:
select COUNT(*)
from TABLENAME
where A= 'a'
and ind IS null;
First handling NULL is different than handling a value:
ind IS NULL
ind = '...'
This makes it difficult to use a prepared statement. But a PreparedStatement should be used, not only for securite (against SQL injection) but also to escape single quotes and such. And is type-safe in that it uses types & conversions.
Oracle SQL has a defect in that it does not distinghuish between NULL and '', so you could go for '' instead. Oracle independent would be:
// Typed fields:
String a = ...;
int n = ...;
String ind = null;
String sql = ind == null
? "select COUNT(*) from TABLENAME where A=? and n=? and ind is null"
: "select COUNT(*) from TABLENAME where A=? and n=? ind = ?";
try (PreparedStatement stmt = new PreparedStatement(sql)) {
stmt.setString(1, a);
stmt.setInt(2, n);
if (ind != null) {
stmt.setString(3, ind);
}
try (ResultSet rs = stmt.executeQuery()) {
long count = rs.next() ? rs.getLong(1) : 0L;
return count;
}
}
Try-with-resources closes statement and result set, also with thrown exception or return in the middle.
For a general Object list, one could use one for loop constructing the SQL template, and a second for setting the PreparedStatement's fields.
This part:
" and ind is'" +info.get(i+1) + " '");
generates the following SQL:
and ind is 'NULL ';
which is wrong because it will throw an error:
ORA-00908: missing NULL keyword
You need to change that to:
" and ind is " +info.get(i+1));
but then it won't work any more for not null values.
I have a problem updating my table from java.
i need to check colmunID(from my table PRODUCTS) = int id(given by user input) and change thats product price in table to one given by user.
PROBLEM:
static void x(int Userid, int Userprice) {
..........................................
String sql = "UPDATE Product set Price = Userprice where ID=Userid; ";
....}
I get error that i don't have column Userprice or Userid in my database. I don't know how to write this to check int User id which is given as argument in this method and not column in my database table which does not exists.
Assuming that you have both the columns with Integer datatype in DB,
String sql = "UPDATE Product set Price="+Userprice+" where ID="+Userid;
You are not passing the actual values to it and the extra ';' is not required. Also, I suggest you to prefer prepared statements, rather than above approach
While you definitely in production code want to use prepared statements to prevent sql injection, an easy fix would be the below.
String sql = String.format("UPDATE Product set Price = %d where ID=%d ",Userprice,Userid);
String wont evaluate variables in itself.
If the table for Userid does not exist in your database, you will not be able to use this in your SQL query. There are two options for you:
1. Pass the Userid and Userprice as a variables to the SQL query
String sql = "UPDATE Product set Price = " + Userprice + "where ID=" + Userid+ "; "
Or
2. Create the table in the database and join on that
String sql = "Update A Set A.Price = b.Userprice FROM Product as A INNER JOIN User as b on A.Userid = b.ID;"
PreparedStatement ps = null;
Connection con = null;
try {
con = getConnection();
String sql = "UPDATE Product set Price = ? where ID= ? ";
ps = con.prepareStatement(sql);
ps.setString(1, Userprice);
ps.setString(2, Userid);
int i = ps.executeUpdate();
if (i > 0) {
System.out.println("Product Updated");
} else {
System.out.println("Error Occured");
}
I think this is something you are looking for... The query should not contain ';' in the String for your code
I am tryin to fetch values from my sql database based on the inputs from a webpage using a select query. I have pasted the query and bit of the code below. The problem is that the query needs input for each and every variable to fetch data since I am using AND. if i dont input a certain value, It will not throw out any output. Please tell me how can I get data from the database even if some of the variables have empty strings.
String host = reqt.getParameter("hostname");
String uuid = reqt.getParameter("cinum");
String cdir = reqt.getParameter("custcode");
String customer = reqt.getParameter("custname");
String mgip = reqt.getParameter("mgmtip");
String cusip = reqt.getParameter("custip");
String bakip = reqt.getParameter("backip");
String ismtick = reqt.getParameter("ismticket");
String tickmean = reqt.getParameter("ticketmean");
String mgtlev = reqt.getParameter("mgmtlvl");
String ptchcat = reqt.getParameter("patchcat");
String ptchsc = reqt.getParameter("patchsch");
String vmsite = reqt.getParameter("site");
String vmcep = reqt.getParameter("cep");
String vmscope = reqt.getParameter("scope");
String os = reqt.getParameter("platform");
String plattype = reqt.getParameter("ostype");
Statement stmt = null;
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/CMS_SCS_PORTAL", "root", "test");
String sql = "Select * from inventory_table where ISNULL(hostname) = '"+host+"' AND ISNULL(uuid) = '"+uuid+"' AND ISNULL(cdir) = '"+cdir+"' AND ISNULL(customer) = '"+customer+"' AND ISNULL(management_ip) = '"+mgip+"' AND ISNULL(customer_ip) = '"+cusip+"' AND ISNULL(backup_ip) = '"+bakip+"' AND ISNULL(ism_ticket_state) = '"+ismtick+"' AND ISNULL(ticket_state_meaning) ='"+tickmean+"' AND ISNULL(management_level) = '"+mgtlev+"' AND ISNULL(patch_category) = '"+ptchcat+"' AND ISNULL(patch_schedule) = '"+ptchsc+"' AND ISNULL(host_site) = '"+vmsite+"' AND ISNULL(VM_Cep) = '"+vmcep+"' AND ISNULL(VM_Scope) = '"+vmscope+"' AND ISNULL(Operating_System) = '"+os+"' AND ISNULL(Operating_System_Type) = '"+plattype+"' LIMIT 10000";
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
Three tips here:
Use prepareStatement, placeholders, and bound parameters.
I think you mean COALESCE(fieldname, '') instead of ISNULL(fieldname)
Pay close attention to data types. I am not sure how MySQL handles strings as Booleans, given that they don't really have a native true Boolean type (I think integers are used internally iirc).
The first step is to mock up your query and try it at the command line in your rdbms and make sure it does what you want it to do. Fix that first. Then rewrite using prepareStatement and bound parameters.
It is not clear what this query is supposed to do in your code sample so unfortunately that's probably the best we can do.
How about using if statements to control creating the select string?
if(! host.isEmpty()) {
sql += "WHERE hostname = '" + host +"'";
}
And so on for every variable...
Consider building the query depending on the available parameters.
To do this, you may populate a Map of pairs "parameter name"/"SQL clause" (we will use PreparedStatement with placeholders, that's why you see ? placeholders).
For each of those parameters, check if it is present and valid in the reqt object.
If it is, append its SQL clause to the query, and store this parameter name in the list .
Once the query is built, create a PreparedStatement with it.
For each stored (valid) parameter, bind its value to its placeholder in the PreparedStatement.
// create a Map of SQL clauses parts
Map<String,String> paramQueryMap = new HashMap<>();
paramQueryMap.put("hostname"," ISNULL(hostname) = ? ");
paramQueryMap.put("cinum"," ISNULL(cinum) = ? ");
// .... and so on
// list of the valid parameters
List<String> foundParameters = new ArrayList<>();
// build the query
StringBuilder builder = new StringBuilder();
builder.append("SELECT * FROM inventory_table");
// browse all the parameters of the map
for (String param : paramQueryMap.keySet()) {
String reqtParam = reqt.getParameter(param);
// valid parameter (not null, not empty), let's add its sql part, and add this parameter to the foundParameters list
if (reqtParam != null && !reqtParam.trim().equals("")) {
foundParameters.add(reqtParam);
if (foundParameters.size() == 0) {// first filter clause, add a WHERE
builder.append(" WHERE ");
} else { // not the first filter, add AND
builder.append(" AND ");
}
// add the sql clause of this parameter
builder.append(paramQueryMap.get(param));
}
}
PreparedStatement statement = conn.prepareStatement(builder.toString());
// replace placeholders for each valid parameter
int paramCounter = 1; // placeholders indexes start at 1
for (String foundParameter : foundParameters) {
statement.setString(paramCounter, reqt.getParameter(foundParameter));
paramCounter++;
}
ResultSet rs = statement.executeQuery();
Please note that this example assumes that the query in your question is correct, and that setString will fit all of your used column types (see the comments and the other answers).
Also note that you could use another Map of "parameter name"/"parameter value" of the valid parameters from the request object, rather than calling getParameter twice for each parameter, but in that case the insertion order will be important and you'd better use a LinkedHashMap.
I need to execute a SQL PreparedStatement in Java using jdbc.
I'm facing problems with one of the parameters because it has SQL content and also Strings from a resource file.
It looks something like this:
Required SQL:
SELECT * FROM Table T WHERE T.value = 10 AND T.display IN ('Sample1', 'Sample2')
In the above query, the Sample1 and Sample2 values must be passed through a parameter to a PreparedStatement.
PreparedStatement:
SELECT * FROM Table T WHERE T.value = 10 ?
In my application code I'm setting the parameters like:
statement.setString(1, "AND T.display IN ('Sample1', 'Sample2')");
However this is not returning the appropriate results.
Is there a better way to build this particular parameter considering it has SQL content and Strings too?
EDIT:
Sample1, Sample2 etc. are strings that are retrieved from an external file at run-time and there can be different number of these strings each time. I.e. there can be only one string Sample1 or multiple strings Sample1, Sample2, Sample3, etc..
EDIT2:
Database being used is Oracle.
The ? placeholder can only be used in a position where a value is expected in the query. Having a ? in any other position (as in your question: WHERE T.value = 10 ?) is simply a syntax error.
In other words: it is not possible to parametrize part of the query itself as you are trying to do; you can only parametrize values. If you need to add a dynamic number of parameters, you will need to construct the query dynamically by adding the required number of parameters and using setString(). For example:
StringBuilder sb = new StringBuilder(
"SELECT * FROM Table T WHERE T.value = 10 AND T.display IN (?");
// Note: intentionally starting at 1, first parameter already above
// Assuming always at least 1 parameter
while (int i = 1; i < params.length; i++) {
sb.append(", ?");
}
sb.append(')');
try (
PreparedStatement pstmt = con.prepareStatement(sb.toString())
) {
for (int i = 0; i < params.length; i++) {
pstmt.setString(i + 1, params[i]);
}
try (
ResultSet rs = pstmt.executeQuery();
) {
// Use resultset
}
}
Use this as PreparedStatement
"SELECT * FROM Table T WHERE T.value = 10 AND T.display IN (?, ?);"
and then call
statement.setString(1, "Sample1");
statement.setString(2, "Sample2");
before executing the statement.
Update:
String generateParamString(int params) {
StringBuilder sb = new StringBuilder("(");
for (int i = 1; i < params; i++) {
sb.append("?, ");
}
sb.append("?)");
return sb.toString();
}
List<String> samples = ... // your list with samples.
String stmtString = "SELECT * FROM Table T WHERE T.value = 10 AND T.display IN "
+ generateParamString(samples.size());
// generate statement with stmtString
for (int i = 0; i < samples.size(); i++) {
statement.setString(i + 1, samples.get(i));
}
// execute statement...