I am planning to execute an update statement using a prepared statement that makes use of a dynamically changing number of columns. for eg: in the first update statement I update only name and age of a table. in the second instance, I update age, city, state, country..etc. in the next instance, I update 150 columns like this.
can someone provide me what is the perfect approach for this in java?
following is the example
If the user provides input for name and age then I update
UPDATE table1 set name = <> ,age = <>;
If the user provides input for city name state country and pin then the update statement should be like this-
UPDATE table1 set name = <>, city = <>,state= <>,country=<>, pin = <>;
Build your sql query like this
update demotable set col1 = case when #col1 is null then col1 else #col1 end
OR
Here #col is passed as value from front end.
from which you may create dynamic sql
declare #col1 nvarchar(max) /// from front you can pass your column value with its column name like: col1 = 'col1'
declare #Query = 'update demotable set = ' + #col1 /// it create query as update demotable set col1 = 'col1'
PREPARE stmt1 FROM #Query ;
EXECUTE stmt1
DEALLOCATE PREPARE stmt1;
I am new to MYSQL but this logic will surely work.
You can write one statement like this:
UPDATE table1
SET name = COALESCE(?, name),
age = COALESCE(?, age),
city = COALESCE(?, city),
. . .
Notes:
This assumes that the values are not being set to NULL.
The ? is a placeholder for a parameter. Don't munge query strings with user input.
Presumably you want a WHERE clause to limit what rows get updated.
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
Here i made table called sub_master with column sub_id and name, insertion and deletion working perfectly fine with this, so put those functions here as well to get reference for update function
and i'm using PostgreSQL for this.
In command line UPDATE query is working fine and query as:
UPDATE school_submaster SET name ='' WHERE sub_id = ;
private void InsertRowActionPerformed(java.awt.event.ActionEvent evt)
{
String query = "INSERT INTO school_submaster (sub_id, \"name\") VALUES ("+SidInput.getText()+",'"+SnameInput.getText()+"')";
executeSQlQuery(query, "Inserted");
}
private void UpdateRowActionPerformed(java.awt.event.ActionEvent evt)
{
String query = "UPDATE school_submaster SET 'name' ='"+SnameInput.getText()+"'+WHERE sub_id = "+SidInput.getText();
executeSQlQuery(query, "Updated");
}
private void DeleteRowActionPerformed(java.awt.event.ActionEvent evt)
{
String query = "DELETE FROM school_submaster WHERE sub_id = "+SidInput.getText();
executeSQlQuery(query, "Deleted");
}
Only use single quotes for string and date constants. Never use single quotes around column names or table names.
Your update is:
UPDATE school_submaster
SET 'name' ='<something>'+WHERE sub_id = "+SidInput.getText();
This has the additional issue of a + in the query string. It should look ore like:
UPDATE school_submaster
SET name = '<something>'
WHERE sub_id = "+SidInput.getText();
But even that is not true. You need to learn to use parameters to pass parameters into queries. The query should be some variant of:
UPDATE school_submaster
SET name = ?
WHERE sub_id = ?
Where the ? is a placeholder for a parameter (it might also be #name or something else).
You are missing a space in the sql WHERE clause, so add it as shown below:
String query = "UPDATE school_submaster SET 'name' ='"+
SnameInput.getText()+"' WHERE sub_id = "+SidInput.getText();
In PostgreSQL user is a reserved keyword that is used in an internal table, however I also have a separate user table in my own database that I need to use. Whenever I try to execute INSERT or UPDATE statements on the table, it generates the following error: The column name 'id' was not found in this ResultSet.
This is the Java code I am currently using:
PreparedStatement stat1 = conn.prepareStatement("SELECT id FROM user;");
PreparedStatement stat2 = conn.prepareStatement("UPDATE user SET date_created = ? , last_updated = ? , uuid = ? WHERE id = ?;");
ResultSet rs = stat1.executeQuery();
while(rs.next()){
UUID uuid = UUID.randomUUID();
String tempId = uuid.toString();
stat2.setTimestamp(1, curDate);
stat2.setTimestamp(2, curDate);
stat2.setString(3, tempId);
stat2.setLong(4,rs.getLong("id"));
stat2.executeUpdate();
}
So my question is, how could I insert or update the values in my personal user table without interfering with the keyword restriction?
Use this:
prepareStatement("UPDATE \"user\" set date_created = ?")
Or, better yet, rename your user table to something else, like users:
ALTER TABLE "user" RENAME TO users;
Escape the table name like this
select * from "user";
I am trying to select data from my db, but instead of getting a certain field I get "".
My table name is locations and it's look like this:
id - int,
location - varchar and time - timestamp.
I would like to select the last location based on the time, here is my code:
this.select = this.conn.createStatement();
ResultSet result = select.executeQuery("SELECT location FROM locations ORDER BY time DESC Limit 1");
result.next();
System.out.println(result.getString(1));
I remind you the output is "";
To identify the column in your resultSet you can use:
result.getString("location");
did u try ?
System.out.println(result.getString(0));
I am wanting to update all the rows in the RESULTSET from the SELECT in one single UPDATE query.
Here is what I have come up with so far:
SELECT id_queue, status FROM table WHERE status IN (0,2) ORDER BY status, id_queue ASC FOR UPDATE;
UPDATE table SET status = 97 WHERE id_queue= " + id_combined + ";
So, I guess I need to take all the id_queue ids, put them together like 1,2,3,4,5,6,7,8 into id_combined.
Any idea the best way to do this in JAVA?
NOTE: I am not just trying to update all with status 0 & 2 to 97, I want to use the select resultset somewhere else.
Not sure what SQL dialect you are using, but this is worth a try:
update table set status = 97 where ID_queue in
(SELECT id_queue FROM table WHERE status IN (0,2))
Use a StringBuilder to collect the id numbers returned in the ResultSet, and then use this to build an update query.
First, execute the query and collect the ID's.
StringBuilder ids = new StringBuilder();
Statement statement = this.conn.createStatement();
statement.executeQuery("SELECT id_queue, status FROM table WHERE status IN (0,2) ORDER BY status, id_queue ASC;");
ResultSet rs = statement.getResultSet();
while (rs.next()) {
if (ids.length() > 0)
ids.append(',');
ids.append(rs.getInt("id_queue"));
}
// use the resultset for whatever else you want
rs.close();
Then, perform the update.
if (ids.length() > 0) {
statement = this.conn.createStatement();
statement.executeUpdate("UPDATE table SET status = 97 WHERE id_queue In ("+ids.toString()+");");
}
One approach to do this is a nested query. That is, you can put a SELECT inside your UPDATE sort of like this:
UPDATE table SET status = 97 WHERE id_queue IN (SELECT id_queue
FROM table
WHERE status IN(0,2))
Google for sql 'nested query' or 'subquery' for more info.
My thought would be something along the lines for (not working code)
List<Integer> ids = new ArrayList<Integer>();
for(Row row : ResultSet.rows()) {
ids.add(row.get("id_queue");
}
String sql = "UPDATE table SET status = 97 WHERE id_queue IN (" +
StringUtils.join(ids, ",") +
")";
Using the StringUtils class from Apache.
Obviously, since you said you don't want to convert ALL items in the ResultSet to status 97:
You can't just have an SQL query that does the SELECT/SET at the same time (unless you can formulate in SQL what the additional criteria are), and
You'll need up modify the for loop to select out only the rows you actually want to modify
For completeness sake, the following would do the update (if you wanted to modify all rows) with pure SQL
UPDATE table SET status = 97
WHERE status IN (0,2)
to avoid the error you need to give the SELECT statment an alias:
try this:
UPDATE table SET status = 97 WHERE id_queue IN (SELECT * FROM (SELECT id_queue
FROM table
WHERE status IN(0,2)) MyResult)
MyResult is an alias to the select query