int selectie = toernooienUitvoer.getSelectedRow();
int selectiec = toernooienUitvoer.getSelectedColumn();
String primarykey = (String) toernooienUitvoer.getValueAt(selectie, 0).toString();
String waarde = toernooienUitvoer.getValueAt(selectie, selectiec).toString();
String columnaam = toernooienUitvoer.getModel().getColumnName(selectiec).toString();
String input = JOptionPane.showInputDialog("wijzig geselecteerde data", waarde);
toernooienUitvoer.setValueAt(input, selectie, selectiec);
PreparedStatement stat = con.prepareStatement("UPDATE fullhouse.toernooi SET ? = ? WHERE toernooi.T_code = ?");
stat.setString(1, columnaam);
stat.setString(2, input);
stat.setString(3, primarykey);
Guys, i know the query is correct, if i input the values. my guess my mistake is somewhere in the preparedstatement
i am getting a MySQLSyntaxErrorException:
As mentioned in other answer, the placeholder ? can only be used for values, not for table and column names. Since you are not reusing the PreparedStatement this is quite simple.
Change from
PreparedStatement stat = con.prepareStatement("UPDATE fullhouse.toernooi SET ? = ? WHERE toernooi.T_code = ?")
to
PreparedStatement stat = con.prepareStatement("UPDATE fullhouse.toernooi SET " + columnName + " = ? WHERE toernooi.T_code = ?")
And adjust the index parameter in the setString calls.
I don't think you can use place holder for dynamically passing the column name,your query should be:
"UPDATE fullhouse.toernooi SET colname = ? WHERE toernooi.T_code = ?"
When you use bind variables, it means the statement is precompiled and on the next executions, it will be faster. You are trying to make the name of the column to be a bind variable, which is not possible.
because you obviously need to update several different columns, in order to achieve some speed, you should declare several prepared statements, one for each column. Keep them in a HashMap<String, PreparedStatement>
The column name of a prepared statement cannot be dynamic because, depending on the column name, the query plan will be wildly different (e.g. sometimes table scan will be the fastest, sometimes using an index, sometimes something even more esoteric).
If SQL can't rely on a certain plan being the fastest, it needs to come up with a new one every time - which means there's no point in making a prepared statement which is why you can't do it.
Related
I am having code something like this.
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
Calculation of fullTableName is something like:
public String getFullTableName(final String table) {
if (this.schemaDB != null) {
return this.schemaDB + "." + table;
}
return table;
}
Here schemaDB is the name of the environment(which can be changed over time) and table is the table name(which will be fixed).
Value for schemaDB is coming from an XML file which makes the query vulnerable to SQL injection.
Query: I am not sure how the table name can be used as a prepared statement(like the name used in this example), which is the 100% security measure against SQL injection.
Could anyone please suggest me, what could be the possible approach to deal with this?
Note: We can be migrated to DB2 in future so the solution should compatible with both Oracle and DB2(and if possible database independent).
JDBC, sort of unfortunately, does not allow you to make the table name a bound variable inside statements. (It has its reasons for this).
So you can not write, or achieve this kind of functionnality :
connection.prepareStatement("SELECT * FROM ? where id=?", "TUSERS", 123);
And have TUSER be bound to the table name of the statement.
Therefore, your only safe way forward is to validate the user input. The safest way, though, is not to validate it and allow user-input go through the DB, because from a security point of view, you can always count on a user being smarter than your validation.
Never trust a dynamic, user generated String, concatenated inside your statement.
So what is a safe validation pattern ?
Pattern 1 : prebuild safe queries
1) Create all your valid statements once and for all, in code.
Map<String, String> statementByTableName = new HashMap<>();
statementByTableName.put("table_1", "DELETE FROM table_1 where name= ?");
statementByTableName.put("table_2", "DELETE FROM table_2 where name= ?");
If need be, this creation itself can be made dynamic, with a select * from ALL_TABLES; statement. ALL_TABLES will return all the tables your SQL user has access to, and you can also get the table name, and schema name from this.
2) Select the statement inside the map
String unsafeUserContent = ...
String safeStatement = statementByTableName.get(usafeUserContent);
conn.prepareStatement(safeStatement, name);
See how the unsafeUserContent variable never reaches the DB.
3) Make some kind of policy, or unit test, that checks that all you statementByTableName are valid against your schemas for future evolutions of it, and that no table is missing.
Pattern 2 : double check
You can 1) validate that the user input is indeed a table name, using an injection free query (I'm typing pseudo sql code here, you'd have to adapt it to make it work cause I have no Oracle instance to actually check it works) :
select * FROM
(select schema_name || '.' || table_name as fullName FROM all_tables)
WHERE fullName = ?
And bind your fullName as a prepared statement variable here. If you have a result, then it is a valid table name. Then you can use this result to build a safe query.
Pattern 3
It's sort of a mix between 1 and 2.
You create a table that is named, e.g., "TABLES_ALLOWED_FOR_DELETION", and you statically populate it with all tables that are fit for deletion.
Then you make your validation step be
conn.prepareStatement(SELECT safe_table_name FROM TABLES_ALLOWED_FOR_DELETION WHERE table_name = ?", unsafeDynamicString);
If this has a result, then you execute the safe_table_name. For extra safety, this table should not be writable by the standard application user.
I somehow feel the first pattern is better.
You can avoid attack by checking your table name using regular expression:
if (fullTableName.matches("[_a-zA-Z0-9\\.]+")) {
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
}
It's impossible to inject SQL using such a restricted set of characters.
Also, we can escape any quotes from table name, and safely add it to our query:
fullTableName = StringEscapeUtils.escapeSql(fullTableName);
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
StringEscapeUtils comes with Apache's commons-lang library.
I think that the best approach is to create a set of possible table names and check for existance in this set before creating query.
Set<String> validTables=.... // prepare this set yourself
if(validTables.contains(fullTableName))
{
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
//and so on
}else{
// ooooh you nasty haker!
}
create table MYTAB(n number);
insert into MYTAB values(10);
commit;
select * from mytab;
N
10
create table TABS2DEL(tname varchar2(32));
insert into TABS2DEL values('MYTAB');
commit;
select * from TABS2DEL;
TNAME
MYTAB
create or replace procedure deltab(v in varchar2)
is
LvSQL varchar2(32767);
LvChk number;
begin
LvChk := 0;
begin
select count(1)
into LvChk
from TABS2DEL
where tname = v;
if LvChk = 0 then
raise_application_error(-20001, 'Input table name '||v||' is not a valid table name');
end if;
exception when others
then raise;
end;
LvSQL := 'delete from '||v||' where n = 10';
execute immediate LvSQL;
commit;
end deltab;
begin
deltab('MYTAB');
end;
select * from mytab;
no rows found
begin
deltab('InvalidTableName');
end;
ORA-20001: Input table name InvalidTableName is not a valid table name ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 21
ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 16
ORA-06512: at line 2
ORA-06512: at "SYS.DBMS_SQL", line 1721
I use prepared statements to read/write data in my DB (SQLite). In my table INVENTORY, there are records which have null value in the column paleta (the column is defined as VARCHAR in the table). I want to select these records and I tried:
sq = "SELECT * FROM INVENTORY WHERE paleta = ? AND product = ? AND lot = ?";
//...
stm = c.prepareStatement(sq);
stm.setNull(1, java.sql.Types.VARCHAR);
stm.setString(2, "theIdOftheProduct");
stm.setString(3, "theLotOftheProduct");
ResultSet rs = stm.executeQuery();
The above query doesn't return anything.. I removed the paleta = ? and I get the records I want.. How can I define the query like SELECT * FROM INVENTORY WHERE paleta is null etc.. using the query parameters?
What you are trying to do is equivalent to writing SELECT * FROM INVENTORY WHERE paleta = NULL ..., which doesn't work.
Since you are essentially searching for rows having a constant value in the paleta column (which happens to be NULL), you can eliminate the first query parameter and explicitly check for null:
sq = "SELECT * FROM INVENTORY WHERE paleta IS NULL AND product = ? AND lot = ?";
stm = c.prepareStatement(sq);
stm.setString(1, "theIdOftheProduct");
stm.setString(2, "theLotOftheProduct");
I found my answer in https://stackoverflow.com/a/4215618/1052284
You'll have to decide upon an unused value. I simply kept it at '' since I don't have empty values.
sq = "SELECT * FROM INVENTORY WHERE IFNULL(paleta, '') = ? AND product = ? AND lot = ?";
//...
stm = c.prepareStatement(sq);
stm.setString(1, ""); // '' for NULL, otherwise a specific value
stm.setString(2, "theIdOftheProduct");
stm.setString(3, "theLotOftheProduct");
But beware if you many queries, it's VERY slow. I clock in at about 4000 times slower, on average, than queries without IFNULL. ~50ms instead of microseconds.
I am using an sql query to add data data to an existing database table.
I want to add data under the columns 'Room_Resource' and 'Quantity'.
The system is designed to allow bookings and i am trying to add bookings made to a tblBookings table, the code below is taken from JButton clicked function.
The value I want to add to Room_Resource is a name taken from a selected table within the system. I declared a variable for this 'resourceChosenString'
The value I want to add to quantity is from the 'Quantity' variable i have declared in relation to a combo box.
Here are my declarations:
int selectedResourceRow = tblResources.getSelectedRow();
Object resourceChosen = tblResources.getValueAt(selectedResourceRow,1);
String resourceChosenString = resourceChosen.toString();
int Quantity = cmbQuantity.getSelectedIndex();
I then have a sql statement:
String sql = ("INSERT INTO tblBookings (Room_Resource,Quantity) VALUES (" + resourceChosenString + " ', ' " + Quantity + " ',) ");
And then the execute code:
try{
pst = conn.prepareStatement(sql);
pst.execute();
JOptionPane.showMessageDialog(null, "Added");
} catch (Exception e){
JOptionPane.showMessageDialog(null, "Error Adding Booking");
}
Currently it gives me an error when I attempt to add the data to the table and wondered if anyone had any suggestions?
Also I considered that perhaps the problem could lie in the fact I have more than two columns in the external table and the table I am adding the data to so columns could be left blank. If this could be the problem, could anyone tell me how to get around it? Possibly if there is a null function I can use instead of values.
You probably want to tell us what database you're using and what error message you're getting. But just off the bat, it looks like your sql string is not formatted correctly. I don't know if you mistyped it in the question or if your code has a simple syntax error.
Just shooting from the hip with what you have, it looks like your sql statement should be:
String sql = "INSERT INTO tblBookings (Room_Resource,Quantity) VALUES ('" + resourceChosenString + "', " + Quantity + ")";
Notice that resourceChosenString should be wrapped in single quotes (you're missing the single quote on the left). Also, I don't think you're supposed to wrap a number in single quotes (I could be wrong since I don't know which database you're using).
Qwerky is right though; you should use a PreparedStatement.
The SQL you are generating is not valid and looks like this;
INSERT INTO tblBookings (Room_Resource,Quantity) VALUES (resource ', ' 1 ',)
^ ^
missing quote extraneous comma
You should tidy it up, or better still use a PreparedStatement.
String sql = "insert into tblBookings (Room_Resource,Quantity) values (?, ?)";
PreparedStatement pst = conn.prepareStatement(sql);
pst.setString(1, resourceChosenString);
pst.setInt(2, quantity); //variable names are not capitalised by convention
pst.execute();
I know this would be a foolish question to ask but still i need to do this.
This is a basic program in java application where I want to use 3 queries simultaneously to print the table.
(I'm not using any Primary key in this case so please help me to resolve this without making my attributes as primary keys - I know this is not a good practice but for now i need to complete it.)
my code:
Connection con = null;
Statement stat1 = null, stat2 = null, stat3 = null;
ResultSet rs1, rs2, rs3;
stat1 = con.createStatement();
stat2 = con.createStatement();
stat3 = con.createStatement();
String str = "\nProduct\tC.P\tS.P.\tStock\tExpenditure\tSales";
info.setText(str);
String s1 = "SELECT type, cp, sp, stock FROM ts_items GROUP BY type ORDER BY type";
String s2 = "SELECT expenditure FROM ts_expenditure GROUP BY type ORDER BY type";
String s3 = "SELECT sales FROM ts_sales GROUP BY type ORDER BY type";
rs1 = stat1.executeQuery(s1);
rs2 = stat2.executeQuery(s2);
rs3 = stat3.executeQuery(s3);
String type;
int cp, sp, stock, expenditure, sales;
while( rs1.next() || rs2.next() || rs3.next() )
{
type = rs1.getString("type");
cp = rs1.getInt("cp");
sp = rs1.getInt("sp");
stock = rs1.getInt("stock");
expenditure = rs2.getInt("expenditure");
sales = rs3.getInt("sales");
info.append("\n" + type + "\t" + cp + "\t" + sp + "\t" + stock + "\t" + expenditure + "\t" + sales);
}
Output:
Runtime Exception: Before start of result set
This is the problem:
while( rs1.next() || rs2.next() || rs3.next() )
If rs1.next() returns true, rs2.next() and rs3.next() won't be called due to short-circuiting. So rs2 and rs3 will both be before the first row. And if rs1.next() returns false, then you couldn't read from that anyway...
I suspect you actually want:
while (rs1.next() && rs2.next() && rs3.next())
After all, you only want to keep going while all three result sets have more information, right?
It's not clear why you're not doing an appropriate join, to be honest. That would make a lot more sense to me... Then you wouldn't be trying to use multiple result sets on a single connection, and you wouldn't be relying on there being the exact same type values in all the different tables.
You do an OR so imagine only one ResultSet has a result.
What you end up with is trying to read from empty result sets.
Suppose rs1 has one result and rs3 has 3 results. Now as per your code it will fail for rs1.getString("type"); during second iteration.
Better to loop over each resultSet separately.
This is going to go badly wrong, in the event that there is a type value that's missing from one of your three tables. Your code just assumes you'll get all of the types from all of the tables. It may be the case for your current data set, but it means that your code is not at all robust.
I would seriously recommend having just one SQL statement, that has each of your three selects as subselects, then joins them all together. Your java can just iterate over the result from this one SQL statement.
I have an SQL query that i am going to run using a PreparedStatement, and it is
UPDATE tbl_HitsCounter SET count = ? WHERE keyid = (SELECT id FROM tbl_HitsMaster WHERE sitename = '?')
Now when i set the 2nd paramater, which is a string value, i am getting a strange SQLException.
preparedStatement.setInt(1, 99);
preparedStatement.setString(2, masterKey);
As the setString() method is executed, i am getting an SQLException
The column position '2' is out of range. The number of columns for this ResultSet is '1'.
I have no idea what this is about, i havent even executed the executeUpdate() method.
There is only one placeholder in your SQL but you are trying to assign a value for the second. Your problem is that you have quoted the second placeholder, your SQL should look more like this:
UPDATE tbl_HitsCounter
SET count = ?
WHERE keyid = (
SELECT id
FROM tbl_HitsMaster
WHERE sitename = ?
)
Note the lack of quotes in sitename = ?. This is a placeholder: ?. This is an SQL question mark string literal: '?'.