I want perform a search operation.I am using CursorAdapter to show data from database.Here is my code
DBHelper
public Cursor fetchDataByName(String string) {
// TODO Auto-generated method stub
Cursor cursor = database.rawQuery("select ABSTRACTS_ITEM._id,ABSTRACTS_ITEM.TITLE,ABSTRACTS_ITEM.TOPIC, "
+ "ABSTRACTS_ITEM.TYPE,ABSTRACTS_ITEM.TEXT"
+ "ABSTRACT_KEY_WORDS.KEYWORDS "
+ "from ABSTRACTS_ITEM,ABSTRACT_KEY_WORDS "
+ "where ABSTRACTS_ITEM._id = ABSTRACT_KEY_WORDS._id "
+ "and ABSTRACT_KEY_WORDS.KEYWORDS like '%" + string + "%'"
+ "or ABSTRACTS_ITEM.TITLE like '%" + string + "%'", null);
return cursor;
}
Inside Activity
cursorAdapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
return dbHelper.fetchDataByName(constraint.toString());
}
});
Previously, I can search data only with Title. Now, I want search data according to Title or keyword. As you can see my query, tried with that. But, it's not working.Because, it shows nothing.
You're missing a , comma and you need to group your OR condition in brackets because AND has a higher precedence.
Cursor cursor = database.rawQuery("select ABSTRACTS_ITEM._id, "
+ "ABSTRACTS_ITEM.TITLE, ABSTRACTS_ITEM.TOPIC, "
+ "ABSTRACTS_ITEM.TYPE, ABSTRACTS_ITEM.TEXT, " // <-- Comma needed
+ "ABSTRACT_KEY_WORDS.KEYWORDS "
+ "from ABSTRACTS_ITEM,ABSTRACT_KEY_WORDS "
+ "where ABSTRACTS_ITEM._id = ABSTRACT_KEY_WORDS._id "
// Group the OR condition with "()"; AND has higher precedence
+ "and (ABSTRACT_KEY_WORDS.KEYWORDS like '%" + string + "%' "
+ "or ABSTRACTS_ITEM.TITLE like '%" + string + "%')", null);
I think you have a simple precedence problem, AND in SQL has a higher precedence than OR. I think you want your SQL to look more like this:
where ABSTRACTS_ITEM._id = ABSTRACT_KEY_WORDS._id
and (
ABSTRACT_KEY_WORDS.KEYWORDS like '%' || ? || '%'
or ABSTRACTS_ITEM.TITLE like '%' || ? || '%'
)
I've also switched to placeholders since using string concatenation to build SQL is far too 1999 for me. This means that you'll want to use the rawQuery(String sql, String\[\] selectionArgs) version of rawQuery.
Related
My sql query looks like this:
String sqlAlloc = " select %1$s from %2$s "
+ "where plot_fk in (%3$s) and plot_fk between ? and ? "
+ "and f001=? "
+ "and repdate=TO_CHAR(TO_DATE(?,'YYYYMMDD'), 'DD-Mon-YY') "
+ "and reportname = ? and change_status in (0,2,6,8,9)";
if (!cond.isEmpty()) {
sqlAlloc += " and C007=?";
}
if (tableName.getKey().equals(ALLOC_PENSIONFUNDS)) {
sqlAlloc += " group by REPDATE, F001, C007, REPORTNAME, COLNAME, ROWNAME";
}
List<String> values = Arrays.asList(tableName.getValue().split(","));
String sqlAllocFormatted = String.format(sqlAlloc,
values.stream().collect(Collectors.joining(",")),
jdbcUsernameMaster + "." + key,
plotFkMasterPublicList.stream()
.collect(Collectors.joining(",")));
try (final Connection conn = ds.getConnection();
final PreparedStatement stmtAlloc =
conn.prepareStatement(sqlAllocFormatted);) {
...
When I scan my code with sonarqube I get the following msg:
This use of PreparedStatement; can be vulnerable to SQL injection (with JDBC)
I don't really understand what is wrong with the sql query and how to fix this?
The vulnerability lies in the fact that you are using String.format to inject things into your query string. If the values of values/tableName and jdbcUsernameMaster, key and plotFkMasterPublicList come from an untrusted source, then this could be a potential source of SQL injection.
To fix this, you either need to not use String.format, but static query strings, or you need to ensure that your values are not from an untrusted source (e.g. user input, external services, etc), and then consciously suppress the warning as a false-positive.
The SQL Injection is really hard to exploit in this particulare case, but if you know the query executed and plotFKMAsterPublicList can be manipultated you can create a "bad query".
What follows is an example based on your original code:
String sqlAlloc = " select %1$s from %2$s "
+ "where plot_fk in (%3$s) and plot_fk between ? and ? "
+ "and f001=? "
+ "and repdate=TO_CHAR(TO_DATE(?,'YYYYMMDD'), 'DD-Mon-YY') "
+ "and reportname = ? and change_status in (0,2,6,8,9)";
List<String> values = Arrays.asList("col1,col2".split(","));
List plotFkMasterPublicList= new ArrayList<>();
plotFkMasterPublicList.add("plot1");
plotFkMasterPublicList.add("plot2");
plotFkMasterPublicList.add("plot3) union all select col1,col2 from user.table union all select col1,col2 from user.table where (1=1 ");
String sqlAllocFormatted = String.format(sqlAlloc,
values.stream().collect(Collectors.joining(",")),
"user" + "." + "table",
plotFkMasterPublicList.stream()
.collect(Collectors.joining(",")));
System.out.println(sqlAllocFormatted);
Thanks to #MarkRotteveel suggestion you can query whatever you want, see these example:
String sqlAlloc = " select %1$s from %2$s "
+ "where plot_fk in (%3$s) and plot_fk between ? and ? "
+ "and f001=? "
+ "and repdate=TO_CHAR(TO_DATE(?,'YYYYMMDD'), 'DD-Mon-YY') "
+ "and reportname = ? and change_status in (0,2,6,8,9)";
List<String> values = Arrays.asList("* from any_table -- col1,col2".split(","));
List plotFkMasterPublicList= new ArrayList<>();
plotFkMasterPublicList.add("plot1");
plotFkMasterPublicList.add("plot2");
String sqlAllocFormatted = String.format(sqlAlloc,
values.stream().collect(Collectors.joining(",")),
"user" + "." + "table",
plotFkMasterPublicList.stream()
.collect(Collectors.joining(",")));
System.out.println(sqlAllocFormatted);
I have created a sqlite table with two columns -Code and Name and stored data in it. User is provided a searchview to enter code or name and the app show results accordingly.
I am using below query to fetch results-
cursor = db.query(TABLE_SEARCH, columns, COLUMN_CODE + " LIKE '" +
searchText + "%' or " + COLUMN_ NAME + " LIKE '%" + searchText + "%'", null, null, null, null)
But the problem is that doesn’t show results in desired order.
I want to show results in below order-
1.On top show results with the Code exactly matching with the searched text.
2.Below that show results with the Name exactly matching with searched text.
3.Below that show results with the Code starting with the searched text.
4.Below that show results with the Name containing the searched text.
If I run multiple sqlite queries it makes the search slow.How can I accomplish my requirement in best way without affecting performance?
The last argument of the query() method is the ORDER BY clause.
Try conditional sorting:
cursor = db.query(
TABLE_SEARCH,
columns,
COLUMN_CODE + " LIKE '" + searchText + "%' OR " +
COLUMN_ NAME + " LIKE '%" + searchText + "%'",
null,
null,
null,
"CASE " +
"WHEN " + COLUMN_CODE + " = '" + searchText + "' THEN 1 " +
"WHEN " + COLUMN_ NAME + " = '" + searchText + "' THEN 2 " +
"WHEN " + COLUMN_CODE + " LIKE '" + searchText + "%' THEN 3 " +
"WHEN " + COLUMN_ NAME + " LIKE '%" + searchText + "%' THEN 4 " +
"END, " + COLUMN_CODE + ", " + COLUMN_ NAME
)
For better performance, you should set indexes for the columns COLUMN_CODE and COLUMN_ NAME.
Also by concatenating so many strings you risk sql injection.
I hope you do check if searchText contains single quotes and change them to double single quotes, like:
searchText = searchText.replace("'", "''");
I want to do a Search function with multiple Java Swing components, where a user can search by (name/nationality/specialty/experience) and results will be displayed in a Jtable.
I'm only struggling with the SQL Query, as if a user typed a 'name' only, no data will be retrieved because it goes to database like this (name, null, null, null) and I don't have any null values in my database.
So I want to retrieve all data with that name regardless of other columns, but at the same time, if they also chose a specific specialty for example, I want to retrieve all data with the selected name AND specialty, and so on.
I hope you understand my question.
My current SQL statement:
public ArrayList<Applications> getData(String name, String nationality, String specialty, String experience) {
ArrayList<Applications> list = new ArrayList<Applications>();
Connection con = getConnection();
Statement st;
ResultSet rows;
try {
st = con.createStatement();
rows = st.executeQuery("SELECT * FROM applications WHERE name LIKE '%" + name+"%'"
+ " AND (nationality LIKE '" + nationality+"')"
+ " AND (specialty LIKE '" + specialty+"')"
+ " AND (experience LIKE '" + experience+"')");
Applications applications;
while(rows.next()) {
applications = new Applications(
rows.getInt("id"),
rows.getString("name"),
rows.getString("nationality"),
rows.getString("Specialty"),
rows.getString("experience")
);
list.add(applications);
}
} catch (SQLException ex) {
Logger.getLogger(MyQuery.class.getName()).log(Level.SEVERE, null, ex);
}
return list;
}
Let's see your query:
rows = st.executeQuery("SELECT * FROM applications WHERE name LIKE '%" + name+"%'"
+ " AND (nationality LIKE '" + nationality+"')"
+ " AND (specialty LIKE '" + specialty+"')"
+ " AND (experience LIKE '" + experience+"')");
Here, since only name was given, the other values are null. If you write this code for testing purpose:
String foo = null;
System.out.println(foo + "");
the output will be
"null"
so, since your values are null, the generated query will be
SELECT * FROM applications WHERE name LIKE '%Rowan Atkinson%'
AND (nationality LIKE 'null')
AND (specialty LIKE 'null')
AND (experience LIKE 'null')
First of all, let's make sure that you get empty String in case of null:
rows = st.executeQuery("SELECT * FROM applications WHERE name LIKE '%" + ((name == null) ? "" : name)+"%'"
+ " AND (nationality LIKE '" + ((nationality == null) ? "" : nationality)+"')"
+ " AND (specialty LIKE '" + ((specialty == null) ? "" : specialty)+"')"
+ " AND (experience LIKE '" + ((experience == null) ? "" : experience)+"')");
The next problem is that you are only putting % at the name, which is also incorrect, so let's fix that:
rows = st.executeQuery("SELECT * FROM applications WHERE name LIKE '%" + ((name == null) ? "" : name)+"%'"
+ " AND (nationality LIKE '%" + ((nationality == null) ? "" : nationality)+"%')"
+ " AND (specialty LIKE '%" + ((specialty == null) ? "" : specialty)+"%')"
+ " AND (experience LIKE '%" + ((experience == null) ? "" : experience)+"%')");
and now read YCF_L's answer so you will use Named Parameters for PreparedStatement.
Below code may give you expected Result.
StringBuilder sql=new StringBuilder("SELECT * FROM applications WHERE 1=1 ");
if(!name.isEmpty()){
sql.append(" AND name LIKE '%" + name+"%'");
}
if(!nationality.isEmpty()){
sql.append(" AND (nationality LIKE '" + nationality+"')");
}
if(!specialty.isEmpty()){
sql.append(" AND (specialty LIKE '" + specialty+"')");
}
if(!experience.isEmpty()){
sql.append(" AND (experience LIKE '" + experience+"')");
}
rows = st.executeQuery(sql.toString());
Try this
In this case You have two Solution :
Check the values if not null append that part to the query, else ignore it
Second solution you can check the value is null or not in the query.
I will gives you an example about the second one, you can use NamedParameterStatement it is like PrepapredStatement to avoid Syntax error and SQL Injection :
String query = "SELECT * FROM applications"
+ " WHERE (:name is null or name LIKE CONCAT('%', :name, '%')) "
+ " AND (:nationality is null or nationality LIKE :nationality)"
+ " AND (:specialty is null or specialty LIKE :specialty)"
+ " AND (:experience is null or experience LIKE :experience)";
NamedParameterStatement namedParametterStatement =
new NamedParameterStatement(connection, query);
namedParametterStatement.setString("name", name);
namedParametterStatement.setString("nationality", nationality);
namedParametterStatement.setString("specialty", specialty);
namedParametterStatement.setString("experience", experience);
Note the trick for example here :
(:nationality is null or nationality LIKE :nationality)
It will check if the nationality is null or the nationality like the value you pass, like this you avoid the null.
Some good references :
Named Parameters for PreparedStatement
I have database with 4 columns int id | String data | String date | int boot and i have some data in it. I have method getRow(String s) when i call it with string for id or data and change query to that option it works but when i´m trying to get row with equal date it won´t pass cursor.moveToFirst condition.
Here is my code:
String CREATE_TABLE = "CREATE TABLE "
+ TABLE_NAME + "(" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + COLUMN_DATA
+ " TEXT," + COLUMN_DATE + " TEXT," + COLUMN_BOOT + " Integer" + ")";
public String getRowID(String id){
SQLiteDatabase db = this.getReadableDatabase();
Cursor c = db.rawQuery("select * from " + TABLE_NAME + " where " + COLUMN_ID + " = " + id, null);
if (c != null && c.moveToFirst()) {
//loggin succes
return "string";
}else return null;
}
public String getRowDate(String date){
SQLiteDatabase db = this.getReadableDatabase();
Cursor c = db.rawQuery("select * from " + TABLE_NAME + " where " + COLUMN_DATE + " = " + date, null);
if (c != null && c.moveToFirst()) {
//loggin succes
return "string";
}else return null;
}
myDb.getRowID("1"); returning something
myDb.getRowDate("02122016"); returning null
I have two rows in my database.
1 | 0.19 | 01122016 | 0
2 | 0.19 | 02122016 | 0
Be wary when comparing integers and strings. You may wonder why SQLite would be comparing integers at all since your arguments are strings, until you consider that your raw query looks like this:
select * from TABLE where DATE = 02122016
That value is interpreted as an integer and converted to text, but it loses the leading zero in the process. You can verify this with a sqlite3 shell:
sqlite> select 02122016;
2122016
sqlite> select '02122016' = 02122016;
0 -- false
sqlite> select cast(02122016 as text);
2122016
The simplest fix is to quote the value using a method from DatabaseUtils:
String escaped = DatabaseUtils.sqlEscapeString(date);
String query = "select * from " + TABLE_NAME + " where " + COLUMN_DATE + " = " + escaped;
A better fix would be to use a placeholder argument instead. Note that Android binds all arguments as strings:
String query = "select * from " + TABLE_NAME + " where " + COLUMN_DATE + " = ?";
db.rawQuery(query, new String[]{date});
However, my advice would be to not use rawQuery() and instead use one of the real query() methods. Here's a good example.
Lastly, perhaps you should consider a different format for storing dates. In practice I usually either store an INTEGER column with a unix timestamp (seconds or milliseconds since epoch), or I use a TEXT column with values in the yyyy-MM-dd format since this is implicitly supported by numerous datetime functions in SQLite.
I have created a table called CHEMISTID:
private static final String CREATE_TABLE_CHEMISTID = "CREATE TABLE "
+ CHEMISTID + "(" + KEY_ID + " INTEGER PRIMARY KEY, " + KEY_CHEMISTID
+ " TEXT" + ")";
My insert function works properly but when I run a search query to find if a chemistId is already present using the following query statement:
String selectQuery = "SELECT * FROM " + CHEMISTID +" WHERE " + KEY_CHEMISTID + " = "+ chemistID + ";";
Cursor c = db.rawQuery(selectQuery,null);
My logcat displays the following error message:
E/AndroidRuntime(1169): FATAL EXCEPTION: main
E/AndroidRuntime(1169): android.database.sqlite.SQLiteException: no such column: Spain (code 1): , while compiling: SELECT * FROM chemistIdTable WHERE chemistId = Spain;
Where Spain is a particular chemistId that I have dynamically created in my program.
How should I fix my selectQuery String so that it searches in the column name KEY_CHEMISTID for a particular String chemistId?
You need to quote your strings such as Spain in SQL so they get taken as string literals and not column name identifiers. You can use single quotes like 'Spain' for that.
However it's much better to use ? parameter placeholders instead and supply the parameter values in the selection args array, like:
... KEY_CHEMISTID + "=?" ...
c = db.rawQuery(selectQuery, new String[] { chemistID });
You missed single quote,So change
String selectQuery = "SELECT * FROM " + CHEMISTID +" WHERE " + KEY_CHEMISTID + " = "+ chemistID + ";";
to
String selectQuery = "SELECT * FROM " + CHEMISTID +" WHERE " + KEY_CHEMISTID + " = '"+ chemistID + "';";
Recommended solution is to use parameterized query as
Cursor c = db.query(CHEMISTID, null, KEY_CHEMISTID + "=?",
new String[] { chemistID },null, null, null, null);