Android cursor fetch understanding - android

I would like to understand a line in a piece of code I saw:
public Cursor fetchMessageByMessageId(String msgId) {
Cursor mCursor =
mDb.query(true, DATABASE_MESSAGES_TABLE, new String[] { KEY_ROWID,
KEY_CONVERSATION_ID, KEY_MSG_ID, KEY_TITLE, KEY_BODY,
KEY_IS_REPLY, KEY_MEDIA_LOC, KEY_URL, KEY_TIMESTAMP },
KEY_MSG_ID + "='" + msgId + "'", null, null, null, null, null);
**if (mCursor != null) {
mCursor.moveToFirst();
}**
return mCursor;
}
The following lines of code in between **
Is this line necessary? I spent 2 hours debugging today finding out why my data was missing when I called something like
while(mCursor.moveTonext())
use the cursor to grab some data and ended up missing the first data always. So I looked at my other parts of the code and realised that I dumped the whole cursor into the adapter so the bold line above had no effect whatsoever. After removing those lines of code everything was good!
So in short, if I just want a cursor with 1 result or many, is it necessary to call the bold statement above? Thanks!

Basically, you get a set of row when you call query(). Initially the cursor will be pointing to nothing. If you call mCursor.moveToNext() or mCursor.moveToFirst(), then you will want to point the the first row. Additional calls to mCursor.moveToNext() will move the cursor to next tuple.
In short, only call mCursor.moveToNext() when you need to get information from next row (if it exists).

Related

How to fix sqlite query error where the ? are not replaced by the selection arguments

My app is a dictionary and now I am working on making it available offline. The database is downloaded and now we have the situation that there is no internet. I need to use the local database. I get my readable database, and then set up a query. On my own testing phone everything works fine. But now before publishing the update I was trying it on two other phones, just to see that the ? in the selection are not replaced by the values in the selectionArgs.
The debugger just shows that the sql statement was built without replacing anything in the selection.
It works on my phone on Android 8.1, but with the two phone that are lower, it stops working and I am out of ideas.
I also tried changing it to db.rawQuery() but that ended with the same result.
String selection = "eng LIKE ? OR oky LIKE ? OR engpl LIKE ? OR okypl LIKE ? OR engcom LIKE ? OR okycom LIKE ? OR Alternative LIKE ?";
String[] selectionArgs = {term, term, term, term, com, com, term};
String orderBy = "eng, oky ASC";
Cursor cursor = db.query(DictionaryContract.WordEntry.TABLE_NAME, null, selection, selectionArgs, null, null, orderBy);
Now I expected the result to give me the terms that fit to the word (term) that was searched for by the user, but instead the cursor is empty, because there is no term containing a question mark.
Especially confusing is that it works on one phone and not on others.
The debugger won't show the resolved ?'s as the replacement of the ?'s is done by the underlying SDK (sqlite implementation) which is in C rather than java.
The best that you can see are the bindArgs e.g. for the following code :-
csr = mDBhlpr.getWritableDatabase().query("table1",null,"id=?",new String[]{"1"},null,null,null);
while (csr.moveToNext()) {
long id = csr.getLong(csr.getColumnIndex(DBHelper.COL_TABLE1_ID));
}
with a breakpoint at the 1st line then you could get :-
I don't believe that the issue is with the binding of the arguments, rather that it is elsewhere. You could test this by changing :-
Cursor cursor = db.query(DictionaryContract.WordEntry.TABLE_NAME, null, selection, selectionArgs, null, null, orderBy);
To
Cursor cursor = db.query(DictionaryContract.WordEntry.TABLE_NAME, null, null, null, null, null, null);
DatabaseUtils.dumpCursor(cursor);
i.e. select all rows, and them write all the rows in the Cursor to the Log.
If still none then the tables on the other devices are empty.
If an Exception occurs then you would have to investigate that.
If this works as expected, then progressively (one by one) add the selection criteria and args.
e.g. first would be
String selection = "eng LIKE ?";
String[] selectionArgs = {term};
String orderBy = "eng, oky ASC";
Cursor cursor = db.query(DictionaryContract.WordEntry.TABLE_NAME, null, selection, selectionArgs, null, null, null);
then :-
String selection = "eng LIKE ? OR oky LIKE ?";
String[] selectionArgs = {term, term};
String orderBy = "eng, oky ASC";
Cursor cursor = db.query(DictionaryContract.WordEntry.TABLE_NAME, null, selection, selectionArgs, null, null,null);
and so on (note finally re-add the order by).
If none of the above works, then your issue (as expected) is elsewhere.

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 Cursor Problem

So I'm trying to get the values from a SQLite database into a cursor, then pick a random value. I can read the cursor with getString() as I normally would in the method, but after it returns the cursor it doesn't work correctly. I don't know why..
Here's my method for getting the cursor from the database. It seems to work correctly.
public Cursor getRandomText(String Rating)
{
Cursor cursor = myDatabase.query("Elec0RandTexts", new String[] {"Message"}, "Rating=?",
new String[]{Rating}, null, null, null);
cursor.moveToFirst();
cursor.close();
return cursor;
}
Here's my code for reading the cursor after it's returned.
Cursor result = dbh.getRandomText(Rating);
result.moveToFirst();
int RandText = rand.nextInt(result.getCount());
result.moveToPosition(RandText);
Toast.makeText(getApplicationContext(), "" + result.getString(RandText), Toast.LENGTH_LONG).show();
result.close();
I'm probably making a stupid mistake and not realizing it, but I can't figure this out.
Thanks,
~Elec0
cursor.close(); // in getRandomText()
after that you cannot obtain any data from the cursor - it is closed. Remove this line.
You close() your Cursor before you return it. From where it is returned to, you are then attempting to call moveToFirst(). This cannot be done if the Cursor is closed.
In your getRandomText(String) method, you should return the meaningful data from your Cursor, rather than the Cursor object itself. That way, the method that created the Cursor can continue to close the Cursor as it should. (It should just happen at the end of the method)

Removing rows from an Android SQLite Cursor

I query and get a result set back, but I need to do some calculations that are impossible in the SQLite WHERE clause in order to determine what shows up in the ListView. How can I remove certain rows from the cursor? I know it is the same question as this Filter rows from Cursor so they don't show up in ListView but that answer does not help. Can an example be provided if there isn't a simpler way to do this?
It might work to simply retain all the rows in the Cursor, but then use a custom adapter to hide the unwanted rows at display time. For example, if you extend CursorAdapter, then you might have something like this in your bindView implementation:
View v = view.findViewById(R.id.my_list_entry);
boolean keepThisRow = .......; // do my calculations
v.setVisibility(keepThisRow ? View.VISIBLE : View.GONE);
There should be a better way to do this, but what I ended up doing is storing the ID of each row I wanted in a string ArrayList, and then requerying where _id IN arraListOfIds.toString(), replacing the square brackets with parentheses to fit SQL syntax.
// Get all of the rows from the database
mTasksCursor = mDbHelper.fetchAllTasks();
ArrayList<String> activeTaskIDs = new ArrayList<String>();
// calculate which ones belong
// .....
if (!hasCompleted)
activeTaskIDs.add(mTasksCursor.getString(TaskerDBadapter.INDEX_ID));
// requery on my list of IDs
mTasksCursor = mDbHelper.fetchActiveTasks(activeTaskIDs);
public Cursor fetchActiveTasks(ArrayList<String> activeTaskIDs)
{
String inClause = activeTaskIDs.toString();
inClause = inClause.replace('[', '(');
inClause = inClause.replace(']', ')');
Cursor mCursor = mDb.query(true, DATABASE_TABLE, columnStringArray(),
KEY_ROWID + " IN " + inClause,
null, null, null, null, null);
if (mCursor != null) { mCursor.moveToFirst(); }
return mCursor;
}
ContentResolver cr = getContentResolver();
Cursor groupCur = cr.query(
Groups.CONTENT_URI, // what table/content
new String [] {Groups._ID, Groups.NAME}, // what columns
"Groups.NAME NOT LIKE + 'System Group:%'", // where clause(s)
null, // ???
Groups.NAME + " ASC" // sort order
);
The "What Columns" piece above is where you can tell the cursor which rows to return. Using "null" returns them all.
I need to do some calculations that
are impossible in the SQLite WHERE
clause
I find this very hard to believe; my experience has been that SQL will let you query for just about anything you'd ever need (with the exception of heirarchical or recursive queries in SQLite's case). If there's some function you need that isn't supported, you can add it easily with sqlite_create_function() and use it in your app. Or perhaps a creative use of the SELECT clause can do what you are looking for.
Can you explain what these impossible calculations are?
EDIT: Nevermind, checking out this webpage reveals that the sqlite_create_function() adapter is all closed up by the Android SQLite wrapper. That's annoying.

Android SQLiteDatabase: Reading one column

I have a one row database just for saving app data. My goal is to read one column (one value) from it.
This query returns all the columns in a Cursor:
public Cursor readAll() {
return getReadableDatabase().query(tableName, null, null, null, null, null, null);
}
It returns a Cursor with one row in it, just perfect. However, I don't want to read all columns at once, because it's slow as I have blob's in db too.
Instead, I'd like to read just one column at a time, separately. For example, for a column called "TEXT" it would be this:
public Cursor readText() {
String[] projection = new String[]{"TEXT"};
return getReadableDatabase().query(tableName, projection, null, null, null, null, null);
}
However, this won't work, as I get back a Cursor with zero rows.
So, how to read a specific column from SQLiteBatabase in Android?
public Cursor readText() {
return getReadableDatabase().rawQuery("SELECT colName FROM myTable", new String[] {});
}
Syntax seems to be correct. Check please that you use right name of the column. Showing the table generation code and actual query code could help.
You can use this one also
public Cursor readText() {
return getReadableDatabase().rawQuery("SELECT column_name FROM table_name", null);
}

Categories

Resources