Android SQLiteDatabase WHERE selection - android

I used an online note taking tutorial to make notes. It works, but I want to make notes based on selection. I thought I did it right, but can someone explain to me what I am doing wrong? I am passing in the course code and I thought I set it up to select correctly. Just tell me what I need to do.
This code seems to be the problem. If selectionargs and where are null it works by returning all notes. I want to return all notes with the where statement to be WHERE courseCode = thisCourse.getCourseID() How do I do this?
(DBOpenHelper.NOTE_COURSE is courseCode)
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String whereClause = DBOpenHelper.NOTE_COURSE+"=?";
String[] selectionArgs = { thisCourse.getCourseID().trim() };
return new CursorLoader(this, NotesProvider.CONTENT_URI, null, whereClause, selectionArgs, null);
}

If selectionargs and where are null it works by returning all notes
If you want to select rows from your table where NOTE_COURSE is null you must use IS NULL:
String whereClause = DBOpenHelper.NOTE_COURSE + " IS NULL";
NOTE: it is NOT an argument value!
EDIT
Where clause is used to limit returned data and as such it is optional and if no special where clause is specified then all rows match the query. This is what happen when you pass null because you simply tell "I do not want rows filered", so all rows are returned.
Also your code looks fine, however I am not sure what you call trim() for setting up selectionArgs.

The answer is simple and it makes me feel so very silly. You need to make your parameters SQL ready by adding single quotes around them as seen in this example.
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String whereClause = DBOpenHelper.NOTE_COURSE+" LIKE " + "'"+thisCourse.getCourseID()+"'";
return new CursorLoader(this, NotesProvider.CONTENT_URI, null, whereClause, null, null);
}

Related

How to sanitize all variables passed to the selectionArgs array?

Veracode Static Scan report points SQL Injection flaw in my Content Provider implementation.
Previously, I posted this question related to all my doubts regarding this flaw.
And after few discussions I came to a conclusion that there might be chances of it being a false positive in the report. Because according to what I researched and read, I was following the security guidelines mentioned in Android docs and other referenced sources to avoid SQL Injection.
There is suggestion everywhere to perform at least some input validation on the data passed to SQL queries.I want to cover this possibility which be the reason of flaw.
Everyone is asking me to sanitize data before passing to query.
How do I exactly sanitize variables passed to selectionArgs array passed to delete(), update() method of Content Provider?
Will DatabaseUtils.sqlEscapeString() be sufficient?
Please suggest!
Here's the implementation where I need to sanitize the variable:
public Loader<Cursor> onCreateLoader(int id, Bundle b) {
switch (id) {
case THOUGHT_LOADER:
return new CursorLoader(getActivity(), NewsFeedTable.CONTENT_URI, NewsFeedTable.PROJECTION, NewsFeedTable._id + "=?", new String[]{tid}, null);
case COMMENT_LOADER:
return new CursorLoader(getActivity(), CommentTable.CONTENT_URI, CommentTable.PROJECTION, CommentTable.COLUMN_TID + "=?", new String[]{tid}, null);
default:
return null;
}
}
Report points to the flaw :Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (CWEID 89) at this line
deleted = db.delete(BulletinTable.TABLE_NAME, selection, selectionArgs); in the below code:
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
if (uri.equals(Contract.BASE_CONTENT_URI)) {
deleteDatabase();
return 1;
}
SQLiteDatabase db = openHelper.getWritableDatabase();
int deleted = 0;
switch (matcher.match(uri)) {
case BULLETIN:
deleted = db.delete(BulletinTable.TABLE_NAME, selection, selectionArgs);
break;
case CLASSROOMS:
deleted = db.delete(ClassroomsTable.TABLE_NAME, selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
if (deleted > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return deleted;
}
Values in the selectionArgs array never need to be sanitized, because they cannot be interpreted as SQL commands (that's the whole point of having separate parameter values).
The purpose of sqlEscapeString() is to format a string so that it can be put into an SQL command (i.e., escape single quotes; all other characters have no meaning inside an SQL string). But when you know that there is a string, you should selectionArgs instead, so this function is not helpful.
You need to sanitize only strings that end up in the SQL command itself. In this case, this would be selection. If this value comes from the user, or from some other app, then you have no control over how much stuff your DELETE statement actually does (it could call SQL functions, or execute subqueries that access other parts of the database).
For practical purposes, it is not possible to sanitize strings that are intended to contain SQL commands, because then you would need a full SQL parser. If your content provider is available for external code, you should allow deleting specific items only through the URI, and disallow custom selections.

database update method unable to accept integer value

This is further to my question earlier, on the db.update.
I'm used the code Karakuri's suggested and modified the database adapter method to the following.
Note that fma in an integer number (34, actually) the database adapter got from mainactivity
public void updateJ(int fma){
String where = "coli = ?";
ContentValues arw = new ContentValues();
cv.put("coli", fma);
the below no longer used but null the last argument instead
// String jusi = Integer.toString(fma);
// String[] whereArgs = new String[] {jusi};
//Log.w(TAG, "whereArgs = " + whereArgs);
db.update("superTable", cv, where, null);
}
It still does not work.
// from previous 'the log.w reads as 06-02 16:24:31.695: W/DBA information(18940): whereArgs = [Ljava.lang.String;#5299a034'
//But I have a question after reading online tutorials and android website and also Karakuri's explanation that the fourth argument whereArgs is suppose to be a String[] instead of a String or an int. But the column 'coli' in superTable is an integer by design. How do I go about making it accept an int rather than a String[]?
I've replaced the whereargs with null and essentially use the cv.put directly using the int fma variable, but it still doesn't work? converted the int to string before putting in and it doesnt work also.
It still does not work. // from previous 'the log.w reads as 06-02 16:24:31.695: W/DBA information(18940): whereArgs = [Ljava.lang.String;#5299a034'
You don't pass the whereArgs to the update(), that's why it "does not work". Change
db.update("superTable", cv, where, null);
to
db.update("superTable", cv, where, whereArgs);
The log output is what one would expect when calling toString() on a String[] string array.

Understanding SQLite Cursor Behavior

I'm writing a method to update default settings in a table. The table is very simple: two columns, the first containing labels to indicate the type of setting, the second to store the value of the setting.
At this point in the execution, the table is empty. I'm just setting up the initial value. So, I expect that this cursor will come back empty. But instead, I'm getting an error (shown below). The setting that I am working with is called "lastPlayer" and is supposed to get stored in the "SETTING_COLUMN" in the "SETTINGS_TABLE". Here's the code:
public static void updateSetting(String setting, String newVal) {
String table = "SETTINGS_TABLE";
String[] resultColumn = new String[] {VALUE_COLUMN};
String where = SETTING_COLUMN + "=" + setting;
System.err.println(where);
SQLiteDatabase db = godSimDBOpenHelper.getWritableDatabase();
Cursor cursor = db.query(table, resultColumn, where, null, null, null, null);
System.err.println("cursor returned"); //I never see this ouput
\\more
}
sqlite returned: error code = 1, msg = no such column: lastPlayer
Why is it saying that there is no such column lastPlayer? I thought that I was telling the query to look at the column "SETTING_COLUMN" and return the record where that column has a value "lastPlayer". I'm confused. Can somebody straighten me out? I've been looking a this for an hour and I just don't see what I am doing wrong.
Thanks!
You're not properly building/escaping your query. Since the value lastPlayer is not in quotes, your statement is checking for equality of two columns, which is what that error message is saying.
To properly build your query, it's best to not do this manually with String concatenation. Instead, the parameter selectionArgs of SQLiteDatabase.query() is meant to do this.
The parameters in your query should be defined as ? and then filled in based on the selectionArgs. From the docs:
You may include ?s in selection, which will be replaced by the values
from selectionArgs, in order that they appear in the selection. The
values will be bound as Strings.
So, your code would look like this:
String where = SETTING_COLUMN + " = ?";
Cursor cursor = db.query(table, resultColumn, where, new String[] { setting }, null, null, null);

android spinner default value using cursor adapter

I am using a spinnerbox in my application. The spinnerbox is to be filled with projects from the database. This itself already works. However i need one extra item in the drop down list.
I want the first item to be "general" general is not a project. Thus it is not retrieved from the database. Is there someway to either inject it in thye cursor or adapter?
What worked for me was to do a UNION in the sql query.
dbStatic.rawQuery(
" SELECT 2 as deftop, typeid as _id, typename as label FROM objtypes UNION "+
" SELECT 1 as deftop, -1 as _id, "+strDefaultSpinner+" as label "+
" ORDER BY deftop asc, label ", null
);
if the item selected is -1, then it's the default value. Otherwise it's a record from the table.
I encountered the same problem a while ago ..
the problem is that you cant actually insert information into a cursor (because its just a pointer) so I believe you have to have some kind of mediator in between ..
my way of solving it was to simply crate a string array [cursor.getCount+1]
then insert your "general" in [0] and then go through your cursor to insert the rest ..
it does go through the items an extra round (which isn't so bad in my case) but for a long list you might want to override the adaptar and insert a line before it goes through the cursor which i cannot help you with the code for that..
I managed to solve this in a differebt way then i originally planned. But it works well. Instead of a general option. I made a checkbox. Is it checked, then its general and the spinner is setunabled. And if unchecked it gets set to enabled. This works for my situation.
My example is working with androidx, room and spinner component.
In your content provider you should have something like.
#Nullable
#Override
public Cursor query(#NonNull Uri uri, #Nullable String[] projection, #Nullable String selection,
#Nullable String[] selectionArgs, #Nullable String sortOrder) {
...
String all = context.getResources().getString(R.string.search_spinner_all);
SimpleSQLiteQuery query = new SimpleSQLiteQuery("SELECT '"+all+"' as "+ThemeData.COLUMN_ID+",'' as "+ThemeData.COLUMN_SET_COUNT+",0 as "+ThemeData.COLUMN_SUBTHEME_COUNT
+",0 as "+ThemeData.COLUMN_YEAR_FROM+",0 as "+ThemeData.COLUMN_YEAR_TO
+" UNION SELECT * FROM " + ThemeData.TABLE_NAME+" ORDER BY "+ ThemeData.COLUMN_ID);
cursor = themeDataDao.selectAll(query);
...
}
In your dao, use
#RawQuery
#Dao
public interface ThemeDataDao {
#RawQuery
Cursor selectAll(SupportSQLiteQuery query);
}
You got it, you can use your simple implementation or cursor adapter !
themesAdapter = new SimpleCursorAdapter(getContext(), R.layout.spinner_with_count, null,
new String[]{ThemeData.COLUMN_ID, ThemeData.COLUMN_SET_COUNT}, new int[] { R.id.spinnerTxLabel, R.id.spinnerTxCount }, 0);
inputTheme.setAdapter(themesAdapter);
LoaderManager.getInstance(this).initLoader(LOADER_THEMES, null, themesLoaderCallback);

SQLite and Android. How do I use String Having in my query?

I have a query that pulls everything from a database into a list view.
return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_HEIGHT,
KEY_BODY, KEY_HOMEID},null , null, null, null, null,null);
| String Having is the 3rd from
last null.
I want only the records where the field KEY_HOMEID matches a variable called journalId which should be a numeric string.
I would also like to know how to bring the variable into my DatabaseAdapter...
would ((resource) this.getApplication()).getjournalId()) work?? resource is a class that extends application.
Firstly, posting error messages rather than "no dice" would be helpful.
Secondly, please read the documentation for SQLiteDatabase.query(). SQL keywords like "FROM" and "HAVING" shouldn't form part of the method call.
You've already passed the table name as the first parameter to query(), so writing "FROM DATABASE_NAME WHERE ..." is both redundant and will lead to an invalid query being formed.
Thirdly, you can't pass in Java variable names as part of the selection parameter String — you need to have a String like: KEY_HOMEID +"="+ journalId.
Fourthly, it's generally a good idea to use selectionArgs rather than embedding the query parameters into the selection. Especially when you have Strings or parameters coming from user input.
e.g. selection = KEY_HOMEID +"=?" and selectionArgs = new String[] { journalId }
Overall, your method call should look like:
mDb.query(DATABASE_TABLE,
new String[] { KEY_ROWID, KEY_HEIGHT,
KEY_BODY, KEY_HOMEID },
KEY_HOMEID +"=?", new String[] { journalId },
null, null, null);
Use a WHERE clause; HAVING is only used with GROUP BY.

Categories

Resources