What's wrong with my SQL update? Java [closed] - java

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
private void subtractCredit(String accountType){ //subtract credit by 1
String CREDITS = "UPDATE CUSTOMERS SET "+accountType+" = "+accountType+" -1, CREDITSUSED=CREDITSUSED+1 WHERE USERNAME='"+username+"'";
try{
ps=con.prepareStatement(CREDITS);
ps.executeUpdate();
}catch(Exception e){
System.out.println(e);
}
}
public String[] getAccount(String accountType){ //Generate a random account.
accountType = "Select * FROM "+accountType+" ORDER BY RAND()";
String[] arr = new String[2];
try{
ps = con.prepareStatement(accountType);
rs = ps.executeQuery();
if(rs.next()){
arr[0] = rs.getString("USERNAME");
arr[1] = rs.getString("PASSWORD");
subtractCredit(accountType);
}
}catch(Exception e){
System.out.println(e);
}
return arr;
}
Here is the catch exception.
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an
error in your SQL syntax; check the manual that corresponds to your
MariaDB server version for the right syntax to use near 'Select * FROM
TABLE ORDER BY RAND() = Select * FROM TABLE ORDER BY RAND() -1' at
line 1
PLEASE HELP!

What's wrong with my SQL update?
The problem is not in your SQL update!!
Look at the SQL in the error message:
Select * FROM TABLE ORDER BY RAND()
Questions you should ask yourself:
Does that look anything like the update SQL? Nope!!
And what is missing from the 'select'? There is no table name!
If getAccount() is the method that is responsible for this error, then the cause should be obvious to anyone who bothers to read the code. The value of accountType is wrong. If the code is excactly as you have shown us, accountType must contain the string "TABLE". That is not going to work because TABLE is an SQL reserved word. A table called TABLE is a schema design error, because it leads to SQL syntax errors.
The other possibility is that the code actually says this:
accountType = "Select * FROM TABLE " +
accountType + " ORDER BY RAND()";
If so, the problem is that you have called the method with an empty string as the account type.
When you have fixed that, I want you to focus on a number of other significant problems in your code:
Assembling SQL by string bashing like this:
accountType = "Select * FROM "+accountType+" ORDER BY RAND()";
is potentially dangerous. If the value of accountType can come from user input, an HTTP request parameter, or anything else that is not under your control, then this code is vulnerable to an SQL injection attack.
The normal solution is to use a constant SQL string with ? placeholders, and then inject the actual parameter values using PreparedStatement.setXxxx method calls. Unfortunately, a table name can't be injected that way.
Catching Exception like you are doing is a BAD IDEA. Sure, it catches the SQLException that you are anticipating. The problem is that it also catches a bunch of other exceptions that you may not be anticipating. For example, if the code in the try block had a bug that caused it to throw a NullPointerException ... you would catch that too.
Using System.out.println(e) to output a "diagnostic" is bad:
For an end user, the exception message is opaque an alarming.
For a developer, you really need a stacktrace.
Sending developer diagnostics to standard output is generally a bad idea. Use a logging framework.
Your error recovery is almost certainly wrong. If the SQL query fails, then your getAccount method returns an String[2] containing null strings. If the calling code doesn't test for this, then you are likely to get an NPE when you try to use the (bogus) account details.
The correct thing to do is most likely to throw another exception, or if you don't want to add code to handle this up-stack, then allow the SQLException to propagate by removing the try catch ... and declaring the exception in the method signature.
This is minor, but most people think that updating the value of a method parameter is bad style. You are doing this when you assign a new value to accountType. Better style would be to declare a local variable and use that to hold the SQL string.

Consider this sequence of statements (drawn from two methods, but the variable names correspond):
getAccount("TABLE"); // inferred
accountType = "Select * FROM "+accountType+" ORDER BY RAND()";
String CREDITS = "UPDATE CUSTOMERS SET "+accountType+" = "+accountType
+ " -1, CREDITSUSED=CREDITSUSED+1 WHERE USERNAME='"+username+"'";
ps=con.prepareStatement(CREDITS);
ps.executeUpdate();
The error indeed occurs in your UPDATE statement, but only part of it appears in the message. The sequence of statements above explains how it got that way. In particular, consider the effect of the assignment to accountType on the subsequent statements.

Related

How to replace a string in a string with integer type in java?

I have a requirement. The technology is quite old doesn't support spring at all . It is pure java application with jdbc connection.
Requirement is :
Suppose
select * from employee where empid = <<empid>> and designation = 'Doctor'
I am trying to replace <> with actual int value in java . How I can do it ?
String query = "select * from employee where empid = <<empid>> and designation = 'Doctor'";
if(query.contains("<<empid>>"))
/// Here I want to replace <<empid>> with actual int value in java
Any leads will be helpful
The code you didn't paste, that actually executes the SQL is either [A] a massive security leak that needs serious rewrites, or [B] is using PreparedStatement.
Here's the problem: SQL injection. Creating the SQL string by mixing a template or a bunch of string constants together with a bunch of user input is a security leak. For example, if you try to make SELECT * FROM users WHERE email = 'foo#bar.com' by e.g. String sql = "SELECT * FROM users WHERE email = '" + email + "'";, the problem is, what if the user puts in the web form, in the 'email' field: whatever#foo.com'; DROP TABLE users CASCADE; EXEC 'FORMAT C: /y /force'; --? Then the SQL becomes:
SELECT * FROM users WHERE email = 'whatever#foo.com'; DROP TABLE users CASCADE; EXEC 'FORMAT C: /y /force'; --';
That is legal SQL and you really, really, really don't want your DB engine to execute it.
Each DB engine has its own ideas on what's actually legal, and may do crazy things such as treating curly quotes as real quotes, etc. So, there is no feasible blacklist or whitelist technology you can think of that will properly cover all the bases: You need to ask your DB engine to do this for you, you can't fix this hole yourself.
Java supports this, via java.sql.PreparedStatement. You instead always pass a fully constant SQL string to the engine, and then fill in the blanks, so to speak:
PreparedStatement ps = con.prepareStatement("SELECT * FROM users WHERE email = ?");
ps.setString(1, "foo#whatever.com");
ps.query();
That's how you do it (and add try-with-resources just like you should already be doing here; statements and resultsets are resources you must always close). Even if you call .setString(1, "foo#whatever.com'; DROP TABLE users CASCADE; --"), then it'll simply look for a row in the database that has that mouthful in the email field. It will not delete the entire users table. Security hole eliminated (and this is the only feasible way to eliminate it).
So, check out that code. Is it using preparedstatement? In that case, well, one way or another that code needs to be calling:
ps.setInt(1, 999);
Where ps is the PreparedStatement object created with connection.prepareStatement(...) where ... is either an SQL constant or at least your input string where the <<empid>> was replaced with a question mark and never with any string input from an untrusted source. The 1 in ps.setInt(1, 999) is the position of the question mark (1 = the first question becomes 999), and the 999 is your actual number. It may look like:
if (input instanceof String) {
ps.setString(idx++, (String) input);
} else if (input instanceof Integer) {
ps.setInt(idx++, ((Integer) input).intValue());
} ...
etcetera. If you don't see that, find the setInt invoke and figure out how to get there. If you don't see any setInt, then what you want is not possible without making some updates to this code.
If you don't even see PreparedStatement anywhere in the code, oh dear! Take that server offline right now, research if a security leak has occurred, if this server stored european data you have 72 hours to notify all users if it has or you can't reasonably figure out e.g. by inspecting logs that it hasn't, or you're in breach of the GDPR. Then rewrite that part using PreparedStatement to solve the problem.

parameterized insert/update in sql

I am trying to insert into a db that I have, and I'd like to do so through parameters. I am connecting to a postgres db using java.
I can connect to the db just fine. I know that because I have various operations that I am using that are already working were I can see, and update existing rows in my db. I am having trouble with INSERT.
I have the following:
private String _update_rentals = "INSERT into rentals (cid, mid) values (?,?)";
private PreparedStatement _update_rentals_statement;
private String _update_movie_status = "UPDATE Movie SET checkedout = true WHERE mid = ?";
private PreparedStatement _update_movie_status_statement;
And I initialize them:
_update_movie_status_statement = _customer_db.prepareStatement(_update_movie_status);
_update_rentals_statement = _customer_db.prepareStatement(_update_rentals);
And
while (movieAvail.next()){
System.out.println(movieAvail.getBoolean(1));
if (movieAvail.getBoolean(1) == false){
//Do chekcout
_update_rentals_statement.clearParameters();
_update_rentals_statement.setInt(1, cid);
_update_rentals_statement.setInt(2, mid);
_update_rentals_statement.executeQuery();
_update_movie_status_statement.clearParameters();
_update_movie_status_statement.setInt(1, mid);
_update_movie_status_statement.executeQuery();
System.out.println("Enjoy your movie!");
}
}
I am getting an error with both of the executeQuery() calls. For some reason I am getting the following error with both:
Exception in thread "main" org.postgresql.util.PSQLException: No results were returned by the query.
I looked at other posts, and I believed that I was following syntax for both insert/ update correctly, so maybe I am overlooking some aspect of this.
This is all part of a larger code base, so I did not want to include the methods these pieces of code are in. But these are the isolated instances which play a part with this code.
In general, when you execute a query, you are willing to retrieve some kind of information from the database. This is usually the case when you are executing SELECT queries. However, with INSERT and UPDATE statements, you are not querying the database, you are simply executing an update or inserting new rows. In the documentation of PreparedStatement you can see in which cases an exception is being thrown when you try to call executeQuery:
Throws: SQLException - if a database access error occurs; this method
is called on a closed PreparedStatement or the SQL statement does not
return a ResultSet object
So in your case the problem is that your statements do not return a ResultSet. You should use execute or executeUpdate instead. The former simply executes the update, while the latter does the same, but also returns the number of affected rows.
I think the main issue is that you are calling executeQuery(), which expects a result to be returned, but Insert/Update are not queries and don't return a result. Try just calling execute().

java looping through multiple sql queries

I'm trying to loop through multiple sql queries that are executed. I want to first get all the question information for a certain task and then get the keywords for that question. I have three records in my Questions table, but when the while loop at the end of list.add(keyword); is done, it jumps to the SELECT Questions.Question loop (as it should) and then just jumps out and gives me only one record and not the other 2.
What am I doing wrong? Can someone maybe help me fix my code? I've thought of doing batch sql executes (maybe that is the solution), but within each while loop, I need information from the previous sql statement, so I can't just do it all at the end of the batch.
SQL Code:
String TaskTopic = eElement.getElementsByTagName("TaskTopic").item(0).getTextContent();
// perform query on database and retrieve results
String sql = "SELECT Tasks.TaskNo FROM Tasks WHERE Tasks.TaskTopic = '" + TaskTopic + "';";
System.out.println(" Performing query, sql = " + sql);
result = stmt.executeQuery(sql);
Document doc2 = x.createDoc();
Element feedback = doc2.createElement("Results");
while (result.next())
{
String TaskNo = result.getString("TaskNo");
// perform query on database and retrieve results
String sqlquery = "SELECT Questions.Question, Questions.Answer, Questions.AverageRating, Questions.AverageRating\n" +
"FROM Questions\n" +
"INNER JOIN TaskQuestions ON TaskQuestions.QuestionID = Questions.QuestionID \n" +
"INNER JOIN Tasks ON Tasks.TaskNo = '" + TaskNo + "';";
result = stmt.executeQuery(sqlquery);
while (result.next())
{
String Question = result.getString("Question");
String Answer = result.getString("Answer");
String AverageRating = result.getString("AverageRating");
String sqlID = "SELECT QuestionID FROM Questions WHERE Question = '" + Question + "';";
result = stmt.executeQuery(sqlID);
while (result.next())
{
String ID = result.getString("QuestionID");
String sqlKeywords = "SELECT Keyword FROM LinkedTo WHERE QuestionID = '" + ID + "';";
result = stmt.executeQuery(sqlKeywords);
while (result.next())
{
String keyword = result.getString("Keyword");
list.add(keyword);
}
}
feedback.appendChild(x.CreateQuestionKeyword(doc2, Question, Answer, AverageRating, list));
}
}
Why this should be done in SQL
Creating loops is exponentially less efficient than writing a sql query. Sql is built to pull back this type of data and can plan out how it is going to get this data from the database (called an execution plan).
Allowing Sql to do its job and determine the best way to pull back the data instead of explicitly determining what tables you are going to use first and then calling them one at a time is better in terms of the amount of resources you will use, how much time it will take to get the results, code readability, and maintainability in the future.
What information you are looking for
In the psuedocode you provided, you are using the Keyword, Question, Answer, and AnswerRating values. Finding these values should be the focus of the sql query. Based on the code you have written, Question, Answer, and AnswerRating are coming from the Questions table and Keyword is coming from the LinkedTo table, so both of these tables should be available to have data pulled from them.
You can note at this point that we have essentially just mapped out what the Select and From portions of your query should look like.
It also looks like you have a parameter called TaskTopic so we need to include the table Tasks to make sure the correct data is returned. Lastly, the TaskQuestions table is the link between the tasks and the questions. Now that we know what the query should look like, let's see what the results are using sql syntax.
The Code
You did not include the declaration of stmt, but I assume that it is a PreparedStatement. You can add parameters to a prepared statement. Notice the ? in the sql code? The parameters you provide will be added in place of the ?. To do this, you should use stmt.setString(1, TaskTopic);. Note that if there were more than one parameter, you would need to add them in the order that they exists in the sql query (using 1, 2, ...)
SELECT l.Keyword,
q.Question,
q.Answer,
q.AverageRating
FROM LinkedTo l Inner Join
Questions q
on l.questionID = q.QuestionID
Where exists ( Select 1
From TaskQuestions tq INNER JOIN
Tasks t
on tq.TaskNo = t.TaskNo
Where t.TaskTopic = ?
and tq.QuestionID = q.QuestionID)
This is one way that you can write the query to return the same results. There are other ways to write this to get what you are looking for.
What's Going On?
There are a few things in this query you may not be familiar with. First are table aliases. Instead of writing the table name over and over again, you can alias your tables. I used the letter q to represent the Questions table. Any time you see q. you should recognize that I am referring to a column from Questions. The q after Questions is what gives the table its alias.
Exists Instead of doing a bunch of inner joins with tables that you are not selecting information from, you can use an exists to check if what you are looking for is in those tables. You can continue to do inner joins if you need data from the tables, but if you don't, Exists is more efficient.
I suspect you had issues with the query before (and probably the one you provided) because you did not provide any information to join TaskQuestions and Tasks together. That most likely resulted in the duplicates. I joined on TaskNo but this may not be the correct column depending on how the tables are set up.

Oracle DB Query Runs in sqlDev but not in Java Program

I have been messing with Oracle DB queries that run from my JAVA app. I can successfully get them all to run in SQL Developer. But when I am trying to execute them from my JAVA app I usually get UpdatadbleResultSet Error/Exception on certain queries.
Also, sometimes I receive, ExhaustedResultset. As I mention at the bottom I will re work the question to break it down(When I get a chance). I keep editing and pretty soon it'll be a book.
Why is this? I cannot seem to pinpoint the problem.
Some queries run successfully such as:
SELECT table_name
FROM all_tables
SELECT column_name, data_length
FROM all_tab_columns
WHERE table_name = 'mytable'
But when I try and run something like
SELECT length(<myColumnName>)
FROM mytable
I get the updateableResultSetError
I am running my queries as methods called on button clicks (example below).
static void testQuery() {
String query = "SELECT blah from blah"
String length;
ResultSet rs = db.runQuery(query);
Length = rs.getString("length(myCol)")
System.out.println(length);
}
I have also tried while rs.next()
I can only think that for some reason I am unable to get into each table and I can only pull the "bigger" picture.
EDIT: Explained DB Connection
I am connecting using some other jarfiles that have been added to my project.
private static IDriver driver = null;
private static Database db = null;
I then pass in all my connection credentials in a separate method.
private void connectDB(){
driver = new OracleDriver();
db = new Database(driver)
driver.getPassword;
driver.getetc;
driver.getEtc;
}
EDIT:
When I getstacktrace all I am returning is.
Ljava.lang.StatckTraceElement;(assortment of random characters).
I may not be getting stack traces right so someone can fill me in. After all I am offering a bounty.
Also I will edit this question and break it down again when I have the time.
Your problem is that you're trying to update a query that can't be updated, hence the updateable result error. It seems that whoever is creating your database connection or executing your query is creating an updatable result set.
You can't use certain types of select in an updatable result set: you can't use aggregated functions (such as length, min, max), you can't use select * etc.)
For the full list see Result Set Limitations and Downgrade Rules
Try retrieving the value in your select statement via the columnIndex instead of the column name and see if that makes a difference.
Currently, its hard to tell what your db.runQuery() does since that code is not posted.
String query = "SELECT length(myCol) FROM myTable";
String length;
ResultSet rs = db.runQuery(query);
while (rs.next()) {
length = rs.getString(1);
System.out.println(length);
}
I've got an inkling what may be happening here (which would explain why some queries work, and some don't). Accoring to the jdbc ResultSet javadocs, when using the getString() method of the result set, the column label.
the label for the column specified with the SQL AS clause.
If the SQL AS clause was not specified, then the label is the name of the column
As "length(myCol)" is neither a label nor a column name, it may be that it fell over because of that (but without stacktrace it is difficult to say what your problem actually is).
Try
String query = "SELECT length(myCol) AS myCol_len FROM myTable"
ResultSet rs = db.runQuery(query);
String length = rs.getString("myCol_len");
Though are you sure, you didn't want:
int length = rs.getInt("myCol_len");
Alternatively (as written by Kal), you can use the column index to get the data from the result set, which oblivates the need for a SQL AS label:
String query = "SELECT length(myCol) FROM myTable"
ResultSet rs = db.runQuery(query);
String length = rs.getString(1);

SQL exception preparing query with ORMLite

I am using an ORM (ORMlite) and all my calls are going well until I get the following error.
Exception in thread "main" org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "
SELECT * FROM ""STORIES"" WHERE ""TITLE"" = 'Deepcut case leads 'NOT FOLLOWED[*]'' "; SQL statement:
SELECT * FROM Stories WHERE title = 'Deepcut case leads 'not followed'' [42000-152]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:327)
at org.h2.message.DbException.get(DbException.java:167)
at org.h2.message.DbException.get(DbException.java:144)
at org.h2.message.DbException.getSyntaxError(DbException.java:179)
at org.h2.command.Parser.getSyntaxError(Parser.java:480)
at org.h2.command.Parser.prepareCommand(Parser.java:229)
at org.h2.engine.Session.prepareLocal(Session.java:426)
at org.h2.engine.Session.prepareCommand(Session.java:374)
at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1093)
at org.h2.jdbc.JdbcPreparedStatement.(JdbcPreparedStatement.java:71)
at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:601)
at com.j256.ormlite.jdbc.JdbcDatabaseConnection.compileStatement(JdbcDatabaseConnection.java:83)
at com.j256.ormlite.stmt.mapped.MappedPreparedStmt.compile(MappedPreparedStmt.java:44)
at com.j256.ormlite.stmt.StatementExecutor.buildIterator(StatementExecutor.java:169)
at com.j256.ormlite.stmt.StatementExecutor.query(StatementExecutor.java:119)
at com.j256.ormlite.dao.BaseDaoImpl.query(BaseDaoImpl.java:189)
I'm confused as to whats going wrong. I am calling the search from these lines:
// get our query builder from the DAO
QueryBuilder<Story, Integer> queryBuilder = StoryDao.queryBuilder();
// the 'title' field must be equal to title (a variable)
queryBuilder.where().eq(Story.TITLE_FIELD_NAME, title);
// prepare our sql statement
PreparedQuery<Story> preparedQuery = queryBuilder.prepare();
// query for all stories that have that title
List<Story> accountList = StoryDao.query(preparedQuery);
Syntax error in SQL statement " SELECT * FROM ""STORIES"" WHERE ""TITLE""...
#bemace is correct that there seem to be quotes in the title that is screwing up the escaping of strings generated by the query.
In ORMLite, you should use the SelectArg feature which will generate a query with SQL ? arguments and then pass the string to the prepared statement directly.
See the documentation on the SelectArg. With it, you'd do something like:
QueryBuilder<Story, Integer> queryBuilder = StoryDao.queryBuilder();
SelectArg titleArg = new SelectArg();
queryBuilder.where().eq(Story.TITLE_FIELD_NAME, titleArg);
PreparedQuery<Story> preparedQuery = queryBuilder.prepare();
titleArg.setValue(title);
List<Story> accountList = StoryDao.query(preparedQuery);
I'm kind of guessing but it looks like there's a problem with the value in the title field, maybe an unescaped quote mark?
I'm not familiar with ORMLite but title = 'Deepcut case leads 'not followed'' doesn't look right. Should probably be "Deepcut case leads 'not followed'" or 'Deepcut case leads \'not followed\'' or some such.
The correct syntax for the statement would be:
SELECT * FROM Stories WHERE title = 'Deepcut case leads ''not followed'' ';
Note the duplicated single quotes inside the string literal.
You will need to tell your ORM layer to follow the ANSI SQL rules for literals.
The exception says that there is some syntactical problem with your generated SELECT statement. Can you print out the generated query? Doing that might help you pin down the exact issue here.
EDIT: Looking closely at your trace shows that string escaping is not handled properly here. Is this your own QueryBuilder? Also, as per this link, are you using SelectArg or directly setting the title?

Categories