Android content provider query IN clause - android

Is possible to use an IN clause for a content provider?
I am currently using
cursor = contentResolver.query(CONTENT_URI, PROJECTION, "field IN (?)", new String[] { argsBuilder.toString() }, null);
If i remove the (?) and just use ? I get an error.
I get 0 count in my cursor.
If I type manually and execute the query in sqlite3 it works.
Help?

When using the IN operator, you need to have one ? separated by a comma per argument you provide in your selectionArgs array. E.g.:
String[] selectionArgs = {"red", "black"};
String selection = "color IN (?, ?)";
The following picks the right count and the proper args:
int argcount = 2; // number of IN arguments
String[] args = new String[]{ 1, 2 };
StringBuilder inList = new StringBuilder(argcount * 2);
for (int i = 0; i < argcount; i++) {
if(i > 0) {
inList.append(",");
}
inList.append("?");
}
cursor = contentResolver.query(
CONTENT_URI,
PROJECTION,
"field IN (" + inList.toString() + ")",
args,
null);

If your arguments are numbers only, this works as well:
Iterable args = ...; // any array, list, set, ...
cursor = contentResolver.query(CONTENT_URI, PROJECTION, "field IN (" + TextUtils.join(",", args) + ")", null, null);

This is a code snippet from my application built in Kotlin, the params array is for illustration purposes only as the parameters come from elsewhere in the code.
The problem with most of the proposed solutions is that they result in the loss of the SQL injection prevention that the use of placeholders provides, so I only use an expression that returns an array of "?" and whose quantity matches the array of provided parameters and then becomes a comma separated String.
That way, the delivered string is a string of placeholders and not the parameters directly.
Sorry for my first answer write in spanish.
var params = arrayOf("1789","1787","1694","1784")
applicationContext.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null,
"_ID IN (" + Array(params.size) { "?" }.joinToString() + ")",
params,
null
)

Related

Multiple SelectionArgs for query

I have a String[] that looks like {1, 2, 3 ..} (a string of IDs).
I want to build a query in Android to obtain all the entries that match the IDs.
Here my code:
Cursor idFoodCursor = getContext().getContentResolver().query(
uriFood,
null,
CookingContract.FoodEntry.COLUMN_NAME + " LIKE ?",
new String[]{selectionArgs},
null
);
if (idFoodCursor.moveToFirst()) {
List<String> ids = new ArrayList<String>();
while (!idFoodCursor.isAfterLast()) {
ids.add(idFoodCursor.getString(idFoodCursor.getColumnIndex(CookingContract.FoodEntry._ID)));
idFoodCursor.moveToNext();
}
idFoodCursor.close();
//Convert the ArrayList in String[]
String[] idSelectionArg = new String[ids.size()];
ids.toArray(idSelectionArg);
return new CursorLoader(
getContext(),
uriFood,
FOOD_COLUMNS,
CookingContract.FoodEntry._ID + " = ?",
idSelectionArg,
sortOrder
);
}
The last query doesn't work because I should add as many "?" as my IDs in the array:
Caused by: java.lang.IllegalArgumentException: Cannot bind argument at index 3 because the index is out of range. The statement has 1 parameters.
How can I fix the problem, taking into account what I want to get? (the correspondence of all the ids in the table)
All the over-mentioned code can be replaced with:
return new CursorLoader(
getContext(),
uriFood,
FOOD_COLUMNS,
"_id IN (SELECT _id FROM food WHERE name LIKE ?)",
new String[] {selectionArgs},
sortOrder
);
That does the job I wanted.
public class Constants {
public static final String JOB_STATUS_CANCELLED = "Cancelled";
public static final String JOB_STATUS_COMPLETE = "Complete";
}
selection = JobListContract.JobEntry.COLUMN_NAME_JOB_STATUS + " NOT IN ( ? , ? ) ";
// The spaces matter!!!!
selectionArgs = new String[]{ Constants.JOB_STATUS_COMPLETE, Constants.JOB_STATUS_CANCELLED };
c = db != null ? db.query(
JobListContract.JobEntry.TABLE_NAME, // The table to query
projection, // The columns to return
selection, // The columns for the WHERE clause
selectionArgs, // The values for the WHERE clause
null, // don't group the rows
null, // don't filter by row groups
JobListContract.JobEntry.COLUMN_NAME_ENTRY_ID + " desc" // The sort order
) : null;

Using brackets and ? in Android SQLiteDatabase query()

I'm trying to execute a query on a SQLiteDatabase in Android, using the query() function. I want to pass the argument in SelectionArgs[], but when I'm using a IN statement, it doesn't seem to substitute the '?' with the correct argument.
My query looks like this:
temp = database.query(TABLE_NAME_ENTRIES,
new String[] {"_id", "Entry", "Summary"},
"_id IN ( ? )",
new String[] {ids}, null, null, null);
and it results in an empty Cursor. Debug gives me the information that the executed query uses a statement "_id IN ( ? )", showing that it doesn't seem to replace the '?' as expected. When I change the query to
temp = database.query(TABLE_NAME_ENTRIES,
new String[] {"_id", "Entry", "Summary"},
"_id IN ( " + ids + " )",
null, null, null, null);
instead, I get the expected result.
I'm really stupid on this problem, any ideas what I'm doing wrong?
You could use a workaround like this - creating a method that generates dynamically your string with ? and , and put it in the query like this:
String[] ids = { "id1", "id2" }; // do whatever is needed first
String query = "SELECT * FROM table"
+ " WHERE _id IN (" + makePlaceholders(ids.length) + ")";
Cursor cursor = mDb.rawQuery(query, ids);
String makePlaceholders(int len) {
if (len < 1) {
// It will lead to an invalid query anyway ..
throw new RuntimeException("No placeholders");
} else {
StringBuilder sb = new StringBuilder(len * 2 - 1);
sb.append("?");
for (int i = 1; i < len; i++) {
sb.append(",?");
}
return sb.toString();
}
}
P.S. I think that the spaces before and after the question mark in your query could be wrong there, but I didn't test it, so I can't be 100% sure

how to retrieve a specific string data from sqlite database by using 2 string arguments?

this is my code used which i use for making method
String item = item1.getText().toString();
item = item.toLowerCase();
String date = getDate();
edited = new Datahelper(this);
edited.open();
String returnedprice = edited.getprice(item,date);
String returneddetail = edited.getdetail(item,date);
edited.close();
price.setText(returnedprice);
details.setText(returneddetail);
and this is my code of method that i am using for getting that string but here i dont know how to use the 2nd date string so that the string price that return is from a row that contains that item and that date.. please give me the code of how to do it..
public String getprice(String item ,String date) {
// TODO Auto-generated method stub
String[] columns = new String[]{KEY_ROWID,
KEY_CATEGORY,KEY_DATE,KEY_PRICE,KEY_DETAILS};
Cursor v =ourDatabase.query(DATABASE_TABLE, columns, KEY_CATEGORY + " ='" + item
+"'",null,null, null, null);
if(v!=null){
String price = v.getString(3);
return price;
}
return null;
}
public String getdetail(String item,String date) {
// TODO Auto-generated method stub
String[] columns = new String[]{KEY_ROWID,
KEY_CATEGORY,KEY_DATE,KEY_PRICE,KEY_DETAILS};
Cursor v =ourDatabase.query(DATABASE_TABLE, columns, KEY_CATEGORY + " ='" + item +
"'",null,null, null, null);
if(v!=null){
String detail = v.getString(4);
return detail;
}
return null;
}
So probably you want to use two arguments in select query so:
You can use two methods:
rawQuery()
query()
I will give you basic example for both cases.
First:
String query = "select * from Table where someColumn = ? and someDateColumn = ?";
Cursor c = db.rawQuery(query, new String[] {textValue, dateValue});
Explanation:
So i recommend to you use ? that is called placeholder.
Each placeholder in select statement will be replaced(in same order so first placeholder will be replaced by first value in array etc.) by values from selectionArgs - it's String array declared above.
Second:
rawQuery() method was easier to understand so i started with its. Query() method is more complex and has a little bit more arguments. So
columns: represents array of columns will be selected.
selection: is in other words where clause so if your selection is
KEY_COL + " = ?" it means "where " + KEY_COL + " = ?"
selectionArgs: each placeholder will be replaced with value from this
array.
groupBy: it's multi-row (grouping) function. more
about
having: this clause is always used with group by clause here is
explanation
orderBy: is clause used for sorting rows based on one or multiple
columns
Also method has more arguments but now you don't need to care about them. If you will, Google will be your friend.
So let's back to explanation and example:
String[] columns = {KEY_COL1, KEY_COL2};
String whereClause = KEY_CATEGORY " = ? and " + KEY_DATE + " = ?";
String[] whereArgs = {"data1", "data2"};
Cursor c = db.query("Table", columns, whereClause, whereArgs, null, null, null);
So whereClause contains two arguments with placeholder for each. So first placeholder will be replaced with "data1" and second with "data2".
When query is performed, query will look like:
SELECT col1, col2 FROM Table WHERE category = 'data1' AND date = 'data2'
Note: I recommend to you have look at Android SQLite Database and ContentProvider - Tutorial.
Also i recommend to you an usage of placeholders which provide safer and much more readable and clear solutions.
You should read any SQL tutorial to find out what a WHERE clause it and how to write it.
In Android, the selection parameter is the expression in the WHERE clause.
Your query could be written like this:
c = db.query(DATABASE_TABLE, columns,
KEY_CATEGORY + " = ? AND " + KEY_DATE + " = ?",
new String[] { item, date },
null, null, null);

Sqlite Query for multiple values in one column

I wanted to do query in table for field id with some vales like 1,5,4,11 which will come from previous screen according to selection.
cursor = database.query(tablename,
new String[] { "TopName" }, "id =?", new String[]{"2,3"}, null, null, null);
When I do like this, I am getting cursor count 0, with new String[]{"2"} I am getting value I want for all ids with values in string array like OR which have value in that column.
You can use the IN operator like this,
cursor = database.query(tablename, new String[] {"TopName"}, "id IN(?,?)",
new String[]{"2","3"}, null, null, null);
The correct syntax for using the IN operator in Android's ContentProvider is as follows:
cursor = database.query(contentUri, projection, "columname IN(?,?)", new String[]{"value1" , "value2"}, sortOrder);
Alternatively, we can also use,
cursor = database.query(contentUri, projection, "columnName IN(?)", new String[] {" 'value1' , 'value2' "}, sortOrder);
Note that we need single quotes around each comma-separated value in the arguments for second case, otherwise the whole string will be treated as one value for the column. The SQL will treat it as
SELECT * FROM table WHERE columnName IN ('value1,value2')
instead of the correct syntax
SELECT * FROM table WHERE columnName IN ('value1' , 'value2')
VolkerK was first to correctly answer the question, but for the sake of completeness here is a full example of how to use the IN operator:
cursor = database.query(tablename,
new String[] { "TopName" }, "id IN (?)", new String[]{"2,3"}, null, null, null);
Use the IN operator instead of equality comparison (=).
For the SelectionArgs section I think you need to change:
new String[]{"2,3"}
To
new String[]{"2","3"}
I would like to put this here since a compendium of answers helped me putting multiple (unknown) values in SQLiteDatabase.query() and the one-question-mark did not work for me. Hope helps anyone
// API > 24
protected String attributesAsMarks(String[] attributes) {
List<String> marks = Collections.nCopies(attributes.length, "?");
return marks.stream().collect(Collectors.joining(","));
}
// I'm using API > 15
protected String attributesAsMarks(String[] attributes) {
StringBuilder sb = new StringBuilder();
String separator = "";
for (String s : attributes) {
if (s == null) continue;
sb.append(separator).append("?");
separator = ",";
}
return sb.toString();
}
Thanks to
#Lalit
https://stackoverflow.com/a/5600690/1358777
https://stackoverflow.com/a/38546936/1358777
https://stackoverflow.com/a/524089/1358777

How to filter the results of content resolver in android?

I would like to get user contacts and then append some kind of regular expression and append them to a list view. I am currently able to get all the contacts via
getContentResolver().query(People.CONTENT_URI, null, null, null, null);
and then pass them to a custom class that extends SimpleCursorAdapter.
So I would like to know how to get only the contacts that match a regular expression and not all of users contacts.
Instead of
getContentResolver().query(People.CONTENT_URI, null, null, null, null);
you should use something like
final ContentResolver resolver = getContentResolver();
final String[] projection = { People._ID, People.NAME, People.NUMBER };
final String sa1 = "%A%"; // contains an "A"
cursor = resolver.query(People.CONTENT_URI, projection, People.NAME + " LIKE ?",
new String[] { sa1 }, null);
this uses a parameterized request (using ?) and provides the actual values as a different argument, this avoids concatenation and prevents SQL injection mainly if you are requesting the filter from the user. For example if you are using
cursor = resolver.query(People.CONTENT_URI, projection,
People.NAME + " = '" + name + "'",
new String[] { sa1 }, null);
imagine if
name = "Donald Duck' OR name = 'Mickey Mouse") // notice the " and '
and you are concatenating the strings.
You can query the content provider with sql type input, the Query method is just a wrapper for an sql command.
Here is an example where I query for a Contacts name given a particular number
String [] requestedColumns = {
Contacts.Phones.NAME,
Contacts.Phones.TYPE
};
Cursor contacts = context.getContentResolver().query(
Contacts.Phones.CONTENT_URI,
requestedColumns,
Contacts.Phones.NUMBER + "='" + phoneNumber + "'",
null, null);
Note that instead of null I have parameters that build up the sql statement.
The requestColumns are the data I want to get back and Contacts.Phones.NUMBER + "='" + phoneNumber + "'" is the Where clause, so I retrieve the Name and Type where the Phone Number matches
You should be able to put a legal SQLite WHERE clause as the third argument to the query() method, including a LIKE, but there's no native REGEXP function in SQLite and Android doesn't seem to let you define your own. So depending how complex your needs are, a set of other SQLite conditions and LIKE expressions might do the trick.
See the documentation on the query method under ContentResolver and SQLite expressions.
Actually REGEXP with Calllog Content Provider works (means that regexp() function is defined for that content provider's Database https://sqlite.org/lang_expr.html#regexp)! But it is very slow: ~15 sec across ~1750 records.
String regexp = "([\\s\\S]{0,}" +
TextUtils.join("||[\\s\\S]{0,}", numbers) +
")";
cursor = context.getContentResolver().query(
CallLog.Calls.CONTENT_URI,
null,
CallLog.Calls.NUMBER + " REGEXP ?",
new String[]{regexp},
CallLog.Calls.DATE + " DESC"
);

Categories

Resources