I am at the verge of finishing my first android app. But I am stuck wondering something about saving special characters in SQLite Database.
When I take data from a edit view and put that data into my database, I am able to input all the special characters without handling anything.
for eg.
EditText editText1 = (EditText) findViewById(R.id.user_name);
String name = editText1.getText().toString();
db.execSQL("INSERT INTO DETAILS VALUES('" + name + "');");
Now the PROBLEM
even if I input ), (, ", * or any other special character, its able to insert that data
BUT as soon as I enter ' (apostrophe), there is an error(Log shows syntax). I have read almost every question related to special characters on stackoverflow but still I am not able to understand that even using ( or ) or " should generate a syntax error, same as in the case of apostrophe. Then why does it happen. Even though it works for me, but I am curios. Also what is the best way to escape these special characters ?
Thanks in advance
Do not use execSQL() for this!
SQLiteDatabase.insert() will build the statement for you, and also avoid any problem with special characters in the parameters.
ContentValues values = new ContentValues();
values.put("FIELD_NAME", name);
db.insert("DETAILS", null, values);
To understand what is happening, just check what is the resulting SQL statement from adding the parameter:
name = Scarlett -> INSERT INTO DETAILS VALUES('Scarlett'); (Ok)
name = O'Hara -> INSERT INTO DETAILS VALUES('O'Hara'); (Wrong)
Since the apostrophe is the string delimiter in SQL, there is one 'O' string plus Hara' which is not a valid token. In this case it just produces an error, but a maliciously crafted string could produce valid SQL which does unintended things.
That's why concatenating strings is a VERY BAD PRACTICE (TM) when writing SQL statements, as it can lead to SQL Injection. Remember the story of Bobby Tables!
To enter an apostrophe, you have to double it ('').
Because an apostrophe is a special character which delimits SQL strings.
So:
String sql = "INSERT INTO DETAILS VALUES('" + name + "')";
db.execSQL(sql.replaceAll("'", "''"));
Even better, you could bind your parameter:
db.execSQL("INSERT INTO DETAILS VALUES (?)", new String[]{name});
The ? is replaced and converted by the parameter passed as new String[]{name}
So, you don't even have to care about the ' characters in your string.
To answer the question in your comments:
A valid SQL String might be:
'This is an apostrophe: ''; this is a quote: "; this is an asterisk: *, ...'
Related
New to java.
I am attempting to write a class that will input a username, run a query on the username to find the ID, and subsequently use that ID in "where clauses" on all my other classes.
This is the statement that I execute (which will only ever return a recordset of a single row):
String sqlStatement = "SELECT AccountHolderId, Passcode from CIS4720.DBO.AccountHolder " +
"where Username = '" + logonName + "'";
Here is my attempt at extracting the ID via the username...
while (rset.next())
{
if(rset.getInt("Username")==logonName){
int whosOnFirst = rset.getInt("AccountHolderId");
}
I saw another answer on the forum that says you can't assign database values to variables. If that is the case, what is a better strategy?
(Also, I realize I'm not parameterizing, but I'd like to get this working before fixing that issue. This is for a course assignment so I am not worried about hack attacks).
P. S. Thanks I fixed the double equals sign (and the extra parenthesis) in the code above.
Here are some comments about the code:
rset.getInt("Username") will get the column Username from the result but it also looks for an Integer column because of getInt. You are not selecting that column in the sql statement so will error out.
If you select it and get a string, use .equals() instead of == to compare string. Also, one = is assignment and == is comparison.
You can use getString to read Strings from the result set.
You don't need to check the username and match it since your query should return exactly that user's data so I would remove the if condition entirely and just have the getInt line there.
I'm trying to create a new MS Access table from a Java program. The SQL query is below. I get an error saying:
net.UcanacessSQLException:UCAExc:::4.0.1 unexpected token: REQ-MTI
PreparedStatement prepStmt;
String createStmt = "CREATE TABLE [" + tableName + "] ([Test_Case_ID] CHAR (20) PRIMARY KEY, [Test_Name] CHAR (120),"
+ " [Test_Description] CHAR (100), [Req-MTI] CHAR (15), [Req-Card_ID] CHAR (50), [Req-H19] CHAR (20));";
prepStmt = conn.prepareStatement(createStmt);
prepStmt.executeUpdate();
I can't use underscores instead of the hyphen. I've tried putting all the table names in square brackets, quotations, ect, but nothing seems to be working. I've taken the generated string and pasted it into a MS Access query and it creates the table fine there.
Any help would be appreciated. Thanks!
I was able to sort-of reproduce your issue under UCanAccess 4.0.2. It seems to be getting confused by the spaces following the CHAR keyword. I could get the code to run if I specified the fields as, e.g., CHAR(20) instead of CHAR (20).
Edit re: question update
I was able to reproduce the issue under UCanAccess 4.0.1. The workaround is to use a Statement instead of a PreparedStatement to execute the DDL. (It's not even necessary to remove the spaces as described above, at least under 4.0.1.)
If column names contain any characters except letters, numbers, and underscores, the name must be delimited by enclosing it in back quotes (`)
so try changing your column name to :
`Req-MTI`
I have the following code:
sql = update [myTable] set content = '" map.getString() + "' where id = " map.getKey();
stmt.executeUpdate(sql);
This is running in a loop, and map.getString() can return a string with single or double quotes in them. I've tried escaping it with multiple quotes around map.getString() (for example
sql = update [myTable] set content = ''" map.getString() + "'' where id = " map.getKey();
But with no luck.
How can I get it to update the content column with the literal value of map.getString()?
Sample error I receive is: (there are many similar ones)
java.sql.SQLException: Incorrect syntax near 's'.
or
java.sql.SQLException: Invalid SQL statement or JDBC escape, terminating ''' not found.
Avoid using concatenate strings of parameter values for building your request:
it is not safe (possible sql injection)
it is not optimized (as the db engine will have always to parse the request even if always the same string is sent to the db)
it will generated lot of bad conversion error (special character etc)
Prefer using PreparedStatement with bind parameters.
Example:
PreparedStatement stmt = connection.prepareStatement("UPDATE mytable SET content = ? WHERE id = ?");
stmt.setString(1, map.getString());
stmt.setInt(2,map.getKey());
stmt.executeUpdate();
Using bind parameters will avoid conversion mistakes and syntax error you are encountering
Use a PreparedStatement. See http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html . They are precomiled, and thus more amenable for executing in loops as well as handling content containing characters which otherwise require special handling.
I've read many threads regarding this topic, but everybody point to the character set in the database.
I'm using strings with special characters like 'ñ' and I'm not able to set them right in the database. As you may guess, the characters are changed to '?'.
The thing is that using this statement, I get it RIGHT:
stmt = con.prepareStatement("INSERT INTO LONG_CODES_TABLE (TIMESTAMP, TABLE_NAME, CODE__C, CODE_DESC)
VALUES (GET_TIMESTAMP, 'MUNICIPIOS', '" + municipio + "', '" + municipio + "') ");
And just in the same database and table, without changing anything, if I use the following I get the '?' character in the DB:
stmt = con.prepareStatement("INSERT INTO LONG_CODES_TABLE (TIMESTAMP, TABLE_NAME, CODE__C, CODE_DESC)
VALUES (GET_TIMESTAMP, 'MUNICIPIOS', ?, ?) ");
stmt.setString(1, municipio);
stmt.setString(2, municipio);
So, the character problem is happening ONLY if I use setString.
Any ideas?
EDIT: The value of the 'municipio' variable is, for example: 'ABADIÑO'.
Later, I can check the differences between doing it on way or the other by asking for that value with an sql statement, for example:
select * from long_codes_table
where table_name = 'MUNICIPIOS' and code__c = 'ABADIÑO'
One way I get the result row. The other way, I don't.
Thank you.
I had that behaviour, too. On top of that I observed that this error did not occur when the application was started from the IDE. That's how I realized that in the JVM - attributes, the one for the encoding was missing.
java %vm-opts% %clspth% -Dfile.encoding=UTF-8 ...
For some sql statements I can't use a prepared statment, for instance:
SELECT MAX(AGE) FROM ?
For instance when I want to vary the table. Is there a utility that sanitizes sql in Java? There is one in ruby.
Right, prepared statement query parameters can be used only where you would use a single literal value. You can't use a parameter for a table name, a column name, a list of values, or any other SQL syntax.
So you have to interpolate your application variable into the SQL string and quote the string appropriately. Do use quoting to delimit your table name identifier, and escape the quote string by doubling it:
java.sql.DatabaseMetaData md = conn.getMetaData();
String q = md.getIdentifierQuoteString();
String sql = "SELECT MAX(AGE) FROM %s%s%s";
sql = String.format(sql, q, tablename.replaceAll(q, q+q), q);
For example, if your table name is literally table"name, and your RDBMS identifier quote character is ", then sql should contain a string like:
SELECT MAX(AGE) FROM "table""name"
I also agree with #ChssPly76's comment -- it's best if your user input is actually not the literal table name, but a signifier that your code maps into a table name, which you then interpolate into the SQL query. This gives you more assurance that no SQL injection can occur.
HashMap h = new HashMap<String,String>();
/* user-friendly table name maps to actual, ugly table name */
h.put("accounts", "tbl_accounts123");
userTablename = ... /* user input */
if (h.containsKey(userTablename)) {
tablename = h.get(userTablename);
} else {
throw ... /* Exception that user input is invalid */
}
String sql = "SELECT MAX(AGE) FROM %s";
/* we know the table names are safe because we wrote them */
sql = String.format(sql, tablename);
Not possible. Best what you can do is to use String#format().
String sql = "SELECT MAX(AGE) FROM %s";
sql = String.format(sql, tablename);
Note that this doesn't avoid SQL injection risks. If the tablename is a user/client-controlled value, you'd need to sanitize it using String#replaceAll().
tablename = tablename.replaceAll("[^\\w]", "");
Hope this helps.
[Edit] I should add: do NOT use this for column values where you can use PreparedStatement for. Just continue using it the usual way for any column values.
[Edit2] Best would be to not let the user/client be able to enter the tablename the way it want, but better present a dropdown containing all valid tablenames (which you can obtain by DatabaseMetaData#getCatalogs()) in the UI so that the user/client can select it. Don't forget to check in the server side if the selection is valid because one could spoof the request parameters.
In this case you could validate the table name against the list of available tables, by getting the table listing from the DatabaseMetaData. In reality it would probably just be easier to use a regex to strip spaces, perhaps also some sql reserved words, ";", etc from the string prior to using something liek String.format to build your complete sql statement.
The reason you can't use preparedStatement is because it is probably encasing the table name in ''s and escaping it like a string.