Android - SQLite Cursor getColumnIndex() is case sensitive? - android

While working with SQLiteCursor in Android I came to know that the getColumnIndex() is behaving case sensitive for example:
Example:
Column Name in DB was: Rules
cursor.getColumnIndex("Rules") //works fine
cursor.getColumnIndex("rules") //throws error, see the error detail
The documentation says nothing about that, for detail please see this.
LogCat says:
java.lang.IllegalStateException: Couldn't read row 0, col -1 from
CursorWindow. Make sure the Cursor is initialized correctly before
accessing data from it
I am confused by this behavior of SQLiteCursor, can someone help me that this is true OR I am doing something wrong? I can provide the code if required.
Thanks.

getColumnIndex() is case sensitive:
Column Name in DB was: Rules
cursor.getColumnIndex("Rules") //workes fine
cursor.getColumnIndex("rules") //throws error, see the error detail

The best and recommended approach using SQLite is that you declare all your table name and column name static, final and class level.. for example:
// write table name
public static final String TABLE_MESSAGE = "messages";
// and column name accordingly
public static final String COLUMN_ID = "_id";
public static final String COLUMN_MESSAGE = "message";
so the benefit of this approach is you don't need to remember the spelling and case etc of the table and column names.
when you access any table or column you simply use these static variables for example:
// TABLE creation sql statement
private static final String TABLE_CREATE = "create table "
+ TABLE_MESSAGE + "( " + COLUMN_ID
+ " integer primary key autoincrement, " + COLUMN_MESSAGE
+ " text not null);";
while querying:
database.query(TABLE_MESSAGE, new String[]{COLUMN_ID,COLUMN_MESSAGE}, null, null, null, null, null);
or it may be used in Cursor
int index = cursor.getColumnIndex(COLUMN_MESSAGE);
this will help you to avoid such conflicts of case sensitivity and spelling mistakes. :)

Another way would be to Query the database itself for the correct name by using PRAGMA table_info, So I wrote a method for just that:
public class database {
private SQLiteDatabase mainDB = null;
private boolean CreateOrOpenDB() {
try {
if (mainDB == null || !mainDB.isOpen()) {
mainDB = Context.openOrCreateDatabase("mainDB", SQLiteDatabase.CREATE_IF_NECESSARY, null);
}
} catch (SQLiteException e) {
return false;
}
return true;
}
private String GetTrueColumnName(String TableName, String column) {
String TrueColName = "";
if (CreateOrOpenDB()) {
try {
Cursor c = mainDB.rawQuery("PRAGMA table_info(" + TableName + ");", null);
if (c != null) {
if (c.moveToFirst()) {
do {
String dbcolumn = c.getString(c.getColumnIndex("name"));
if (column.toLowerCase().equals(dbcolumn.toLowerCase())) {
TrueColName = dbcolumn;
break;
}
} while (c.moveToNext());
}
c.close();
}
mainDB.close();
} catch (Exception e) {
}
}
return TrueColName;
}
}
then all you need to call is:
String CorrectName = GetTrueColumnName(TableName, "RuLeS");
and yes, I know it will be hard on the database. But it works and is stable

return readableDatabase
.query(
ProductosContract.ProductosEntry.TABLE_NAME,
ProductosContract.ProductosEntry.ALL_COLUMNS_NAME_ALIAS, null, null, null, null, null
)
You can specify the columns to retrieve, in that parameter add column name alias to lower case like (Kotlin):
arrayOf("name as 'name'")
So you will get always the lowercase one. Use the lower case or the one you prefer, it will work.

Related

Android SQLite check if table exist [duplicate]

I have an android app that needs to check if there's already a record in the database, and if not, process some things and eventually insert it, and simply read the data from the database if the data does exist. I'm using a subclass of SQLiteOpenHelper to create and get a rewritable instance of SQLiteDatabase, which I thought automatically took care of creating the table if it didn't already exist (since the code to do that is in the onCreate(...) method).
However, when the table does NOT yet exist, and the first method ran upon the SQLiteDatabase object I have is a call to query(...), my logcat shows an error of "I/Database(26434): sqlite returned: error code = 1, msg = no such table: appdata", and sure enough, the appdata table isn't being created.
Any ideas on why?
I'm looking for either a method to test if the table exists (because if it doesn't, the data's certainly not in it, and I don't need to read it until I write to it, which seems to create the table properly), or a way to make sure that it gets created, and is just empty, in time for that first call to query(...)
EDIT
This was posted after the two answers below:
I think I may have found the problem. I for some reason decided that a different SQLiteOpenHelper was supposed to be created for each table, even though both access the same database file. I think refactoring that code to only use one OpenHelper, and creating both tables inside it's onCreate may work better...
Try this one:
public boolean isTableExists(String tableName, boolean openDb) {
if(openDb) {
if(mDatabase == null || !mDatabase.isOpen()) {
mDatabase = getReadableDatabase();
}
if(!mDatabase.isReadOnly()) {
mDatabase.close();
mDatabase = getReadableDatabase();
}
}
String query = "select DISTINCT tbl_name from sqlite_master where tbl_name = '"+tableName+"'";
try (Cursor cursor = mDatabase.rawQuery(query, null)) {
if(cursor!=null) {
if(cursor.getCount()>0) {
return true;
}
}
return false;
}
}
I know nothing about the Android SQLite API, but if you're able to talk to it in SQL directly, you can do this:
create table if not exists mytable (col1 type, col2 type);
Which will ensure that the table is always created and not throw any errors if it already existed.
Although there are already a lot of good answers to this question, I came up with another solution that I think is more simple. Surround your query with a try block and the following catch:
catch (SQLiteException e){
if (e.getMessage().contains("no such table")){
Log.e(TAG, "Creating table " + TABLE_NAME + "because it doesn't exist!" );
// create table
// re-run query, etc.
}
}
It worked for me!
This is what I did:
/* open database, if doesn't exist, create it */
SQLiteDatabase mDatabase = openOrCreateDatabase("exampleDb.db", SQLiteDatabase.CREATE_IF_NECESSARY,null);
Cursor c = null;
boolean tableExists = false;
/* get cursor on it */
try
{
c = mDatabase.query("tbl_example", null,
null, null, null, null, null);
tableExists = true;
}
catch (Exception e) {
/* fail */
Log.d(TAG, tblNameIn+" doesn't exist :(((");
}
return tableExists;
Yep, turns out the theory in my edit was right: the problem that was causing the onCreate method not to run, was the fact that SQLiteOpenHelper objects should refer to databases, and not have a separate one for each table. Packing both tables into one SQLiteOpenHelper solved the problem.
// #param db, readable database from SQLiteOpenHelper
public boolean doesTableExist(SQLiteDatabase db, String tableName) {
Cursor cursor = db.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '" + tableName + "'", null);
if (cursor != null) {
if (cursor.getCount() > 0) {
cursor.close();
return true;
}
cursor.close();
}
return false;
}
sqlite maintains sqlite_master table containing information of all tables and indexes in database.
So here we are simply running SELECT command on it, we'll get cursor having count 1 if table exists.
You mentioned that you've created an class that extends SQLiteOpenHelper and implemented the onCreate method. Are you making sure that you're performing all your database acquire calls with that class? You should only be getting SQLiteDatabase objects via the SQLiteOpenHelper#getWritableDatabase and getReadableDatabase otherwise the onCreate method will not be called when necessary. If you are doing that already check and see if th SQLiteOpenHelper#onUpgrade method is being called instead. If so, then the database version number was changed at some point in time but the table was never created properly when that happened.
As an aside, you can force the recreation of the database by making sure all connections to it are closed and calling Context#deleteDatabase and then using the SQLiteOpenHelper to give you a new db object.
Kotlin solution, based on what others wrote here:
fun isTableExists(database: SQLiteDatabase, tableName: String): Boolean {
database.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '$tableName'", null)?.use {
return it.count > 0
} ?: return false
}
public boolean isTableExists(String tableName) {
boolean isExist = false;
Cursor cursor = db.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '" + tableName + "'", null);
if (cursor != null) {
if (cursor.getCount() > 0) {
isExist = true;
}
cursor.close();
}
return isExist;
}
no such table exists: error is coming because once you create database with one table after that whenever you create table in same database it gives this error.
To solve this error you must have to create new database and inside the onCreate() method you can create multiple table in same database.
Important condition is IF NOT EXISTS to check table is already exist or not in database
like...
String query = "CREATE TABLE IF NOT EXISTS " + TABLE_PLAYER_PHOTO + "("
+ KEY_PLAYER_ID + " TEXT,"
+ KEY_PLAYER_IMAGE + " TEXT)";
db.execSQL(query);
i faced that and deal with it by try catch as simple as that i do what i want in table if it not exist will cause error so catch it by exceptions and create it :)
SQLiteDatabase db=this.getWritableDatabase();
try{
db.execSQL("INSERT INTO o_vacations SELECT * FROM vacations");
db.execSQL("DELETE FROM vacations");
}catch (SQLiteException e){
db.execSQL("create table o_vacations (id integer primary key ,name text ,vacation text,date text,MONTH text)");
db.execSQL("INSERT INTO o_vacations SELECT * FROM vacations");
db.execSQL("DELETE FROM vacations");
}
.....
Toast t = Toast.makeText(context, "try... " , Toast.LENGTH_SHORT);
t.show();
Cursor callInitCheck = db.rawQuery("select count(*) from call", null);
Toast t2a = Toast.makeText(context, "count rows " + callInitCheck.getCount() , Toast.LENGTH_SHORT);
t2a.show();
callInitCheck.moveToNext();
if( Integer.parseInt( callInitCheck.getString(0)) == 0) // if no rows then do
{
// if empty then insert into call
.....

Android sqlite - cursor count not 0 on empty table

I have the following code in a bigger project:
final class DBlifetimeStatisticsHandler{ //implements DBvalueHandler<Cyclist, Double>{
private final String TAG = getClass().getName();
private static final boolean debug = true;
private final DBminMaxAvgHandler dbMinMaxAvgHandler = new DBminMaxAvgHandler();
// table name
private static final String TABLE_LIFETIME_STATISTICS = "lifetime_statistics";
// column names
private static final String KEY_LIFETIME_STATISTICS_ID = "lifetime_statistics_id";
private static final String KEY_MIN_MAX_AVG = "min_max_avg";
// table create statement
private static final String CREATE_TABLE = "CREATE TABLE "
+ TABLE_LIFETIME_STATISTICS + "("
+ KEY_LIFETIME_STATISTICS_ID + " LONG PRIMARY KEY NOT NULL,"
+ KEY_MIN_MAX_AVG + " LONG"
+ ")";
public void onCreateTable(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}
public void onUpgrade(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_LIFETIME_STATISTICS);
onCreateTable(db);
}
public long addValue(SQLiteDatabase db, Statistics Statistics ) {
ContentValues values = new ContentValues();
long ID = getLatestID(db)+1;
values.put(KEY_STATISTICS_ID, ID);
... //not important to the question
}
private long getLatestID(SQLiteDatabase db){
String selectQuery = "SELECT MAX(" + KEY_STATISTICS_ID +") FROM " + TABLE_STATISTICS;
Cursor c = db.rawQuery(selectQuery, null);
c.moveToFirst();
int id = 0;
Log.e("count", String.valueOf(c.getCount()));
if (c.moveToFirst()){
...
}
return id;
}
}
After I updated the table it is created again. So when I try to add a new value I had problems cause it always jumped into the if clause because c.moveToFirst() always returned true.
So I tried to tried to check if c.getCount() would return true but sadly it does always return 1. So the question is: Why would it return 1 on an empty table? (I do use Questoid SQLite Browser and the table is really empty)
You use aggregate function MAX, so read documentation:
There are two types of simple SELECT statement - aggregate and non-aggregate queries. A simple SELECT statement is an aggregate query if it contains either a GROUP BY clause or one or more aggregate functions in the result-set.
An aggregate query without a GROUP BY clause always returns exactly one row of data, even if there are zero rows of input data.
It might be some kind of a buggy behavior when using MAX. Check this link too Android database (SQLite) returns a non-empty cursor from an empty table
this is my solution
public Boolean isNotEmpty(){
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_STATISTICS, null);
while (cursor.moveToNext() ) {
return true;
}
return false;
}
You are getting a result with one row in your Cursor because that is what you requested.
The result is a single column called MAX with a value that will be the max id of all the rows in your table. In your case of an empty table, this value is null.
I am using group by to resolve this. Please check my example :
SELECT COUNT(*) FROM " + TABLE_NAME + " WHERE isSynced=0 group by isSynced
I resolve this probme this way:
SELECT COUNT(*) AS numero, MAX(tagua_lagps) as tmp_max_lagps, MAX(tagua_logps) as tmp_max_logps, MIN(tagua_lagps) as tmp_min_lagps, MIN(tagua_logps) as tmp_min_logps FROM TAB_AGUA
On empty table, c.getCount(); gives 1 but values are NULL. But numero (c.getString(c.getColumnIndex("numero")) has a value of 0.
So rather than checking c.getCount() you must check the result of count(*).

Check if some string is in SQLite database

I have some trouble with a SQLite database with 1 table and 2 columns, column_id and word. I extended SQLiteAssetHelper as MyDatabase and made a constructor:
public MyDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
I need to check whether some string is in the database (in column word). I tried to modify the code from answer provided by Benjamin and dipali, but I used SQLiteAssetHelper and I can't get it to work. The method that I have in mind receives the string to search for as a parameter and returns a boolean if string is in the database.
public boolean someMethod(String s)
In addition, I tried to put the check on a background thread with AsyncTask because I have 60 strings to check.
TABLE_NAME and COLUMN_WORD should be self-explanatory.
public boolean someMethod(String s) {
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[] {COLUMN_WORD};
String where = COLUMN_WORD + " = ?";
String[] whereArgs = new String[] {s};
// select column_word from table where column_word = 's' limit 1;
Cursor cursor = db.query(TABLE_NAME, columns, where, whereArgs, null, null, null, "1");
if (cursor.moveToFirst()) {
return true; // a row was found
}
return false; // no row was found
}
You can do this in the background, but I don't think for a query like this it's even necessary.
EDIT
There are some improvements that should be made to the above for the sake of correctness. For one thing, the Cursor should be closed since it is no longer being used. A try-finally block will ensure this:
Cursor cursor = db.query(...);
try {
return cursor.moveToFirst();
} finally {
cursor.close();
}
However, this method doesn't need to obtain a whole `Cursor. You can write it as follows and it should be more performant:
public boolean someMethod(String s) {
SQLiteDatabase db = getReadableDatabase();
String sql = "select count(*) from " + TABLE_NAME + " where "
+ COLUMN_WORD + " = " + DatabaseUtils.sqlEscapeString(s);
SQLiteStatement statement = db.compileStatement(sql);
try {
return statement.simpleQueryForLong() > 0;
} finally {
statement.close();
}
}
You could add a catch block and return false if you think it's possible (and valid) to encounter certain exceptions like SQLiteDoneException. Also note the use of DatabaseUtils.sqlEscapeString() because s is now concatenated directly into the query string and thus we should be wary of SQL injection. (If you can guarantee that s is not malicious by the time it gets passed in as the method argument, then you could theoretically skip this, but I wouldn't.)
because of possible data leaks best solution via cursor:
Cursor cursor = null;
try {
cursor = .... some query (raw or not your choice)
return cursor.moveToNext();
} finally {
if (cursor != null) {
cursor.close();
}
}
1) From API KITKAT u can use resources try()
try (cursor = ...some query)
2) if u query against VARCHAR TYPE use '...' eg. COLUMN_NAME='string_to_search'
3) dont use moveToFirst() is used when you need to start iterating from beggining
4) avoid getCount() is expensive - it iterates over many records to count them. It doesn't return a stored variable. There may be some caching on a second call, but the first call doesn't know the answer until it is counted.

Sqlite verify if value of column exists

i'm wondering if this method is right to verify if the value of _username already exists in the column "username"
public boolean verification(String _username) throws SQLException{
Cursor c = dataBase.rawQuery("SELECT * FROM "+TABLE_NAME+" WHERE "+KEY_USERNAME+"="+_username, null);
if (c!=null)
return true; // return true if the value of _username already exists
return false; // Return false if _username doesn't match with any value of the columns "Username"
}
Is there a better way to do the same thing, i'm really not sure about this, it seemed right for me.
Thanks.
Beware of SQL injection attacks! You should always use a parameterized query:
Cursor c = dataBase.rawQuery("SELECT 1 FROM "+TABLE_NAME+" WHERE "+KEY_USERNAME+"=?", new String[] {_username});
(Honestly I'm not sure how your first query didn't throw an exception since you forgot to wrap the string in quotes...)
Also rawQuery() will always return a Cursor, you must check if the Cursor is empty, not null.
As for "the best" approach, this works fine, but I recommend closing the Cursor to free up resources. All together:
public boolean verification(String _username) {
Cursor c = dataBase.rawQuery("SELECT 1 FROM "+TABLE_NAME+" WHERE "+KEY_USERNAME+"=?", new String[] {_username});
boolean exists = c.moveToFirst();
c.close();
return exists;
}
Is there a better way to do the same thing, i'm really not sure about
this, it seemed right for me. Thanks.
In the terms of security and purity yes, for sure.
public boolean verification(String _username) throws SQLException {
int count = -1;
Cursor c = null;
try {
String query = "SELECT COUNT(*) FROM "
+ TABLE_NAME + " WHERE " + KEY_USERNAME + " = ?"
c = dataBase.rawQuery(query, new String[] {_username});
if (c.moveToFirst()) {
count = c.getInt(0);
}
return count > 0;
}
finally {
if (c != null) {
c.close();
}
}
}
I recommend you to an usage of ? that is called placeholder. Each placeholder will be replaced with value from string array in the same order. This is called also parametrized statement as a defence agains SQL injection. When your work with Cursor is finished, release it.

Android: Deleting specific row in database

Sorry if this seems obvious. I'm trying to write a method to delete a row from a String showId. What would be the best way, and can Cursors only be used for Selects or also for Deletes and Updates?
These are the two methods I'm at so far:
public int deleteShowById1(String showId){
Cursor cursor = db.rawQuery("DELETE FROM tblShows WHERE showId = '" + showId+"'", null);
if (cursor.moveToFirst()) {
return 1;
} else
return -1;
}
public int deleteShowById2(String showId) {
String table_name = "tblShows";
String where = "showId='"+showId+"'";
return db.delete(table_name, where, null);
}
As we know from mysql query, it is same here in android.
String query = "DELETE FROM " +TABLE_NAME+ " WHERE " + COLUM_NAME+ " = " + "'"+VALUE +"'" ;
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL(query);
db.close();
VALUE may or may not have single quotation depending on datatype.
I tend to use the second method (db.delete), as I think using rawQuery is frowned upon.
If you do a select, then loop through the cursor to do updates or deletes, that would make sense, but to pass a cursor to do the delete or update doesn't make sense to me, as the program won't know how to parse the cursor results to get the correct fields.

Categories

Resources