Are there any methods that assist with resolving common SQLite issues? - android-sqlite

It is often that relatively simple errors cause issues often compounded by a misunderstanding of SQLite.
For example:-
tables and columns not found because it is often assumed that the onCreate method of a DBHelper (subclass of the SQLiteOpenHelper class) runs every time an instance of the DBHelper is created or every time the App is run. (Note! onCreate is only automatically invoked when a Database is first created and then only when an attempt is made to use one of the SQLiteDatabase getReadableDatabase or getWriteableDatabse methods, if changing the database structure/schema then there are 3 simple ways of forcing onCreate to run, a) Clear the App's Data, b) uninstall the App or b) if the onUpgrade method invokes the onCreate method (after dropping the table(s)) then increase the Database Version Number).
Inserts/Updates not working but not failing.
For the novice to SQLite it can also be infuriating/daunting not being able to readily see what the database contains.
So are there any common utilities that can assist?
Note! this is intended to be a question for sharing knowledge Q & A style.

The following are some common utilities that the novice might find helpful, they are designed to be non-specific and work on any database/table.
Currently there are the following potentially useful methods:-
getAllRowsFromTable to retreive a Cursor with all rows.
logCursorColumns to write the Columns in a Cursor to the log.
logCursorData to write the Cursor Data and Columns to the log.
Note! Requests/suggestions for additions/amendments will definitely be considered.
Additionally there are some methods used by the above or were used for testing/creating the above :-
getEmptyColumnLessCursor (100 points for a use of this method :) ).
getBytedata to return a hex string of the given byte array (for BLOB handling).
convertBytesToHex to convert a byte array into a hex string (for BLOB handling).
getAllRowsFromTable
The intended use is for creating a cursor to subsequently be examined by the logCursorColumns and logCursorData methods. This should not be used in a production App (not that it can't be).
This has the signature :-
public static Cursor getAllRowsFromTable(
SQLiteDatabase db,
String tablename,
boolean use_error_checking,
String forceRowidAs)
Where:-
The first parameter is the SQLite Database (required due to the generic nature).
The second parameter is the table name from which to get the data.
The third parameter, if true will check if the table exists before creating the cursor writing to the log if the table is not in the database.
The fourth parameter, if not null or if a string of length > 0, will create an additional column, named according to the parameter with the contents of the ROWID (intended for assisting when no alias for ROWID has been provided and hence ROWID is not included). What's ROWID??
Example usage:-
mEventsDBHelper = new EventsDBHelper(this);
// Get all rows from table (this exist or should do)
Cursor events = CommonSQLiteUtilities.getAllRowsFromTable(
mEventsDBHelper.getEventsDB(),
EventsDBHelper.TBNAME,
CommonSQLiteUtilities.ERROR_CHECKING_ON,
null
);
// Deisgned to be problematic i.e. no such table
Cursor ooops = CommonSQLiteUtilities.getAllRowsFromTable(
mEventsDBHelper.getEventsDB(),
"NOTATABLE",
CommonSQLiteUtilities.ERROR_CHECKING_ON,
"rumplestiltskin"
);
The second invocation results in the following line in the log:-
D/SQLITE_CSU: Table NOTATABLE was not located in the SQLite Database Master Table.
A Cursor will always be returned although that Cursor may have no rows and no columns (e.g. in the case where the table was not in the database).
Exceptions are trapped and reported on when the database table is queried (if it is). e.g specifying IS as the 4th parameter (effectively trying to use ROWID AS IS, which SQLIte doesn't like, will result in something similar to :-
10-09 18:57:52.591 3835-3835/? E/SQLiteLog: (1) near "IS": syntax error
10-09 18:57:52.592 3835-3835/? D/SQLITE_CSU: Exception encountered but trapped when querying table events Message was:
near "IS": syntax error (code 1): , while compiling: SELECT rowid AS IS , * FROM events
10-09 18:57:52.592 3835-3835/? D/SQLITE_CSU: Stacktrace was:
10-09 18:57:52.592 3835-3835/? W/System.err: android.database.sqlite.SQLiteException: near "IS": syntax error (code 1): , while compiling: SELECT rowid AS IS , * FROM events
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:889)
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:500)
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:44)
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1316)
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1163)
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1034)
10-09 18:57:52.592 3835-3835/? W/System.err: at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1202)
10-09 18:57:52.592 3835-3835/? W/System.err: at mjt.sqlitedbexamples.CommonSQLiteUtilities.getAllRowsFromTable(CommonSQLiteUtilities.java:97)
10-09 18:57:52.592 3835-3835/? W/System.err: at mjt.sqlitedbexamples.MainActivity.onCreate(MainActivity.java:61)
10-09 18:57:52.593 3835-3835/? W/System.err: at android.app.Activity.performCreate(Activity.java:5990)
10-09 18:57:52.593 3835-3835/? W/System.err: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
10-09 18:57:52.593 3835-3835/? W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
10-09 18:57:52.593 3835-3835/? W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
10-09 18:57:52.593 3835-3835/? W/System.err: at android.app.ActivityThread.access$800(ActivityThread.java:151)
10-09 18:57:52.593 3835-3835/? W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
10-09 18:57:52.593 3835-3835/? W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102)
10-09 18:57:52.593 3835-3835/? W/System.err: at android.os.Looper.loop(Looper.java:135)
10-09 18:57:52.593 3835-3835/? W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5254)
10-09 18:57:52.593 3835-3835/? W/System.err: at java.lang.reflect.Method.invoke(Native Method)
10-09 18:57:52.593 3835-3835/? W/System.err: at java.lang.reflect.Method.invoke(Method.java:372)
10-09 18:57:52.593 3835-3835/? W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
10-09 18:57:52.593 3835-3835/? W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
The Cursor returned will be empty with no rows or columns.
logCursorColumns
Is intended to be used to write column information about a cursor (which would normally reflect the database, especially if using getAllRowsFromTable).
This has the signature :-
public static void logCursorColumns(Cursor csr)
Where :-
The first parameter is a Cursor (any Cursor).
Example usage :-
Note! Following on from the previous example.
CommonSQLiteUtilities.logCursorColumns(events);
CommonSQLiteUtilities.logCursorColumns(ooops);
This will result in output along the lines of :-
10-09 18:57:52.593 3835-3835/? D/SQLITE_CSU: logCursorColumns invoked. Cursor has the following 8 columns.
10-09 18:57:52.593 3835-3835/? D/SQLITE_CSU: Column Name 1 is _id
10-09 18:57:52.593 3835-3835/? D/SQLITE_CSU: Column Name 2 is starts
10-09 18:57:52.593 3835-3835/? D/SQLITE_CSU: Column Name 3 is starts_timestamp
10-09 18:57:52.593 3835-3835/? D/SQLITE_CSU: Column Name 4 is ends
10-09 18:57:52.593 3835-3835/? D/SQLITE_CSU: Column Name 5 is ends_timestamp
10-09 18:57:52.593 3835-3835/? D/SQLITE_CSU: Column Name 6 is description
10-09 18:57:52.593 3835-3835/? D/SQLITE_CSU: Column Name 7 is counter
10-09 18:57:52.593 3835-3835/? D/SQLITE_CSU: Column Name 8 is bytedata
10-09 18:57:52.593 3835-3835/? D/SQLITE_CSU: logCursorColumns invoked. Cursor has the following 0 columns.
logCursorData
This is very similar to logCursorColumns other than it provides more extensive information regrading columns and that it also provides information regrading the actual data stored in the database.
This has the signature :-
public static void logCursorData(Cursor csr)
Where :-
The first parameter is a Cursor (any Cursor).
Example usage :-
Note! Following on from the previous example.
CommonSQLiteUtilities.logCursorData(events);
CommonSQLiteUtilities.logCursorData(ooops);
This will result in output along the lines of :-
10-09 19:30:31.801 1455-1455/? D/SQLITE_CSU: logCursorData Cursor has 6 rows with 8 columns.
10-09 19:30:31.801 1455-1455/? D/SQLITE_CSU: Information for row 1 offset=0
For Column _id Type is INTEGER value as String is 1 value as long is 1 value as double is 1.0
For Column starts Type is INTEGER value as String is 1507537831 value as long is 1507537831 value as double is 1.507537831E9
For Column starts_timestamp Type is INTEGER value as String is 1507537831783 value as long is 1507537831783 value as double is 1.507537831783E12
For Column ends Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column ends_timestamp Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column description Type is STRING value as String is TESTEVENT 001 just description value as long is 0 value as double is 0.0
For Column counter Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column bytedata Type is BLOB value as String is unobtainable! value as long is unobtainable! value as double is unobtainable! value as blob is 00000000
10-09 19:30:31.802 1455-1455/? D/SQLITE_CSU: Information for row 2 offset=1
For Column _id Type is INTEGER value as String is 2 value as long is 2 value as double is 2.0
For Column starts Type is INTEGER value as String is 1507537831 value as long is 1507537831 value as double is 1.507537831E9
For Column starts_timestamp Type is INTEGER value as String is 1507537831785 value as long is 1507537831785 value as double is 1.507537831785E12
For Column ends Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column ends_timestamp Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column description Type is STRING value as String is TESTEVENT 002 description and bytearray value as long is 0 value as double is 0.0
For Column counter Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column bytedata Type is BLOB value as String is unobtainable! value as long is unobtainable! value as double is unobtainable! value as blob is 0001020304050607080900
10-09 19:30:31.802 1455-1455/? D/SQLITE_CSU: Information for row 3 offset=2
For Column _id Type is INTEGER value as String is 3 value as long is 3 value as double is 3.0
For Column starts Type is INTEGER value as String is 1507537831 value as long is 1507537831 value as double is 1.507537831E9
For Column starts_timestamp Type is INTEGER value as String is 1507537831789 value as long is 1507537831789 value as double is 1.507537831789E12
For Column ends Type is INTEGER value as String is 15254678 value as long is 15254678 value as double is 1.5254678E7
For Column ends_timestamp Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column description Type is STRING value as String is TESTEVENT 003 desc, bytes and endint value as long is 0 value as double is 0.0
For Column counter Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column bytedata Type is BLOB value as String is unobtainable! value as long is unobtainable! value as double is unobtainable! value as blob is 7F7E7D7C
10-09 19:30:31.802 1455-1455/? D/SQLITE_CSU: Information for row 4 offset=3
For Column _id Type is INTEGER value as String is 4 value as long is 4 value as double is 4.0
For Column starts Type is INTEGER value as String is 1507537831 value as long is 1507537831 value as double is 1.507537831E9
For Column starts_timestamp Type is INTEGER value as String is 1507537831792 value as long is 1507537831792 value as double is 1.507537831792E12
For Column ends Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column ends_timestamp Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column description Type is STRING value as String is TESTEVENT 004 desc, bytes and endlong value as long is 0 value as double is 0.0
For Column counter Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column bytedata Type is BLOB value as String is unobtainable! value as long is unobtainable! value as double is unobtainable! value as blob is 38424C56606A747E
10-09 19:30:31.803 1455-1455/? D/SQLITE_CSU: Information for row 5 offset=4
For Column _id Type is INTEGER value as String is 5 value as long is 5 value as double is 5.0
For Column starts Type is INTEGER value as String is 1507537831 value as long is 1507537831 value as double is 1.507537831E9
For Column starts_timestamp Type is INTEGER value as String is 1507537831794 value as long is 1507537831794 value as double is 1.507537831794E12
For Column ends Type is INTEGER value as String is 1699999999 value as long is 1699999999 value as double is 1.699999999E9
For Column ends_timestamp Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column description Type is STRING value as String is TESTEVENT 005 desc, endint value as long is 0 value as double is 0.0
For Column counter Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column bytedata Type is BLOB value as String is unobtainable! value as long is unobtainable! value as double is unobtainable! value as blob is 00000000
10-09 19:30:31.803 1455-1455/? D/SQLITE_CSU: Information for row 6 offset=5
For Column _id Type is INTEGER value as String is 6 value as long is 6 value as double is 6.0
For Column starts Type is INTEGER value as String is 1507537831 value as long is 1507537831 value as double is 1.507537831E9
For Column starts_timestamp Type is INTEGER value as String is 1507537831796 value as long is 1507537831796 value as double is 1.507537831796E12
For Column ends Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column ends_timestamp Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column description Type is STRING value as String is TESTEVENT 006 desc, endlong value as long is 0 value as double is 0.0
For Column counter Type is INTEGER value as String is 0 value as long is 0 value as double is 0.0
For Column bytedata Type is BLOB value as String is unobtainable! value as long is unobtainable! value as double is unobtainable! value as blob is 00000000
and for the empty Cursor (ooops) :-
10-09 19:30:31.804 1455-1455/? D/SQLITE_CSU: logCursorData Cursor has 0 rows with 0 columns.
The Code CommonSQLiteUtilities.java :-
public class CommonSQLiteUtilities {
public static final boolean ERROR_CHECKING_ON = true;
public static final boolean ERROR_CHECKING_OFF = false;
// SQLite MASTER TABLE definitions
static final String SQLITE_MASTER = "sqlite_master";
static final String SM_TABLE_TYPE_COLUMN = "type";
static final String SM_NAME_COLUMN = "name";
static final String SM_TABLENAME_COLUMN = "tbl_name";
static final String SM_ROOTPAGE_COLUMN = "rootpage";
static final String SM_SQL_COLUMN = "sql";
static final String SM_TYPE_TABLE = "table";
static final String SM_TYPE_INDEX = "index";
static final String CSU_TAG = "SQLITE_CSU";
private CommonSQLiteUtilities() {}
/**
* Generic get all rows from an SQlite table,
* allowing the existence of the table to be checked and also
* allowing the ROWID to be added AS a supplied string
*
* #param db The SQLiteDatabase
* #param tablename The name of the table from which the
* returned cursor will be created from;
* Note!
* #param use_error_checking Whether ot not to try to detect errors
* currently just table doesn't exist,
* true to turn on, false to turn off
* ERROR_CHECKING_ON = true
* ERROR_CHECKING_OFF = false
* #param forceRowidAs If length of string passed is 1 or greater
* then a column, as an alias of ROWID, will be
* added to the cursor
* #return the extracted cursor, or in the case of the
* underlying table not existing an empty cursor
* with no columns
*/
public static Cursor getAllRowsFromTable(SQLiteDatabase db,
String tablename,
boolean use_error_checking,
String forceRowidAs) {
String[] columns = null;
// Tablename must be at least 1 character in length
if (tablename.length() < 1) {
Log.d(CSU_TAG,new Object(){}.getClass().getEnclosingMethod().getName() +
" is finishing as the provided tablename is less than 1 character in length"
);
return new MatrixCursor(new String[]{});
}
// If use_error_checking is true then check that the table exists
// in the sqlite_master table
if (use_error_checking) {
Cursor chkcsr = db.query(SQLITE_MASTER,null,
SM_TABLE_TYPE_COLUMN + "=? AND "
+ SM_TABLENAME_COLUMN + "=?",
new String[]{SM_TYPE_TABLE,tablename},
null,null,null
);
// Ooops table is not in the Database so return an empty
// column-less cursor
if (chkcsr.getCount() < 1) {
Log.d(CSU_TAG,"Table " + tablename +
" was not located in the SQLite Database Master Table."
);
// return empty cursor with no columns
return new MatrixCursor(new String[]{});
}
chkcsr.close();
}
// If forcing an alias of ROWID then user ROWID AS ???, *
if(forceRowidAs != null && forceRowidAs.length() > 0) {
columns = new String[]{"rowid AS " +forceRowidAs,"*"};
}
// Finally return the Cursor but trap any exceptions
try {
return db.query(tablename, columns, null, null, null, null, null);
} catch (Exception e) {
Log.d(CSU_TAG,"Exception encountered but trapped when querying table " + tablename +
" Message was: \n" + e.getMessage());
Log.d(CSU_TAG,"Stacktrace was:");
e.printStackTrace();
return new MatrixCursor(new String[]{});
}
}
/**
* Create and return a Cursor devoid of any rows and columns
* Not used, prehaps of very little use.
* #param db The Sqlite database in which the cursor is to be created
* #return The empty Cursor
*/
private static Cursor getEmptyColumnLessCursor(SQLiteDatabase db) {
return new MatrixCursor(new String[]{});
}
/**
* Write column names in the passed Cursor to the log
* #param csr The Cursor to be inspected.
*/
public static void logCursorColumns(Cursor csr) {
Log.d(CSU_TAG,
new Object(){}.getClass().getEnclosingMethod().getName() +
" invoked. Cursor has the following " +
Integer.toString(csr.getColumnCount())+
" columns.");
int position = 0;
for (String column: csr.getColumnNames()) {
position++;
Log.d(CSU_TAG,"Column Name " +
Integer.toString(position) +
" is "
+ column
);
}
}
/**
* Write the contents of the Cursor to the log
* #param csr The Cursor that is to be displayed in the log
*/
public static void logCursorData(Cursor csr) {
int columncount = csr.getColumnCount();
int rowcount = csr.getCount();
int csrpos = csr.getPosition(); //<<< added 20171016 to
Log.d(CSU_TAG,
new Object(){}.getClass().getEnclosingMethod().getName() +
" Cursor has " +
Integer.toString(rowcount) +
" rows with " +
Integer.toString(columncount) + " columns."
);
csr.moveToPosition(-1); //Ensure that all rows are retrieved <<< added 20171016
while (csr.moveToNext()) {
String unobtainable = "unobtainable!";
String logstr = "Information for row " + Integer.toString(csr.getPosition() + 1) + " offset=" + Integer.toString(csr.getPosition());
for (int i=0; i < columncount;i++) {
logstr = logstr + "\n\tFor Column " + csr.getColumnName(i);
switch (csr.getType(i)) {
case Cursor.FIELD_TYPE_NULL:
logstr = logstr + " Type is NULL";
break;
case Cursor.FIELD_TYPE_FLOAT:
logstr = logstr + "Type is FLOAT";
break;
case Cursor.FIELD_TYPE_INTEGER:
logstr = logstr + " Type is INTEGER";
break;
case Cursor.FIELD_TYPE_STRING:
logstr = logstr + " Type is STRING";
break;
case Cursor.FIELD_TYPE_BLOB:
logstr = logstr + " Type is BLOB";
break;
}
String strval_log = " value as String is ";
String lngval_log = " value as long is ";
String dblval_log = " value as double is ";
String blbval_log = "";
try {
strval_log = strval_log + csr.getString(i);
lngval_log = lngval_log + csr.getLong(i);
dblval_log = dblval_log + csr.getDouble(i);
} catch (Exception e) {
strval_log = strval_log + unobtainable;
lngval_log = lngval_log + unobtainable;
dblval_log = dblval_log + unobtainable;
try {
blbval_log = " value as blob is " +
getBytedata(csr.getBlob(i),24);
} catch (Exception e2) {
e2.printStackTrace();
}
}
logstr = logstr + strval_log + lngval_log + dblval_log + blbval_log;
}
Log.d(CSU_TAG,logstr);
}
csr.moveToPosition(csrpos); // restore cursor position <<< added 20171016
}
/**
* Return a hex string of the given byte array
* #param bytes The byte array to be converted to a hexadecimal string
* #param limit the maximum number of bytes;
* note returned string will be up to twice as long
* #return The byte array represented as a hexadecimal string
*/
private static String getBytedata(byte[] bytes, int limit) {
if (bytes.length < limit) {
return convertBytesToHex(bytes);
} else {
byte[] subset = new byte[limit];
System.arraycopy(bytes,0,subset,0,limit);
return convertBytesToHex(subset);
}
}
// HEX characters as a char array for use by convertBytesToHex
private final static char[] hexarray = "0123456789ABCDEF".toCharArray();
/**
* Return a hexadecimal string representation of the passed byte array
* #param bytes The byte array to be represented.
* #return The string representing the byte array as hexadecimal
*/
private static String convertBytesToHex(byte[] bytes) {
char[] hexstr = new char[bytes.length * 2];
for (int i=0; i < bytes.length; i++) {
int h = bytes[i] & 0xFF;
hexstr[i * 2] = hexarray[h >>> 4];
hexstr[i * 2 + 1] = hexarray[h & 0xF];
}
return new String(hexstr);
}
}
To use the code simply create the CommonSQLiteUtilities class and copy the code above.

Addition 1 - logDatabaseInfo
This will write some information about the Database to the log, for example:-
10-12 06:32:12.426 20575-20575/? D/SQLITE_CSU: DatabaseList Row 1 Name=main File=/data/data/mjt.sqlitedbexamples/databases/eventsDB
10-12 06:32:12.426 20575-20575/? D/SQLITE_CSU: Database Version = 1
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table Name = android_metadata Created Using = CREATE TABLE android_metadata (locale TEXT)
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table = android_metadata ColumnName = locale ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table Name = events Created Using = CREATE TABLE events(_id INTEGER PRIMARY KEY, starts INTEGER DEFAULT (strftime('%s','now')),starts_timestamp INTEGER DEFAULT 0,ends INTEGER DEFAULT 0,ends_timestamp INTEGER DEFAULT 0,description TEXT, counter INTEGER DEFAULT 0,bytedata BLOB DEFAULT x'00000000')
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table = events ColumnName = _id ColumnType = INTEGER Default Value = null PRIMARY KEY SEQUENCE = 1
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table = events ColumnName = starts ColumnType = INTEGER Default Value = strftime('%s','now') PRIMARY KEY SEQUENCE = 0
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table = events ColumnName = starts_timestamp ColumnType = INTEGER Default Value = 0 PRIMARY KEY SEQUENCE = 0
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table = events ColumnName = ends ColumnType = INTEGER Default Value = 0 PRIMARY KEY SEQUENCE = 0
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table = events ColumnName = ends_timestamp ColumnType = INTEGER Default Value = 0 PRIMARY KEY SEQUENCE = 0
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table = events ColumnName = description ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table = events ColumnName = counter ColumnType = INTEGER Default Value = 0 PRIMARY KEY SEQUENCE = 0
10-12 06:32:12.427 20575-20575/? D/SQLITE_CSU: Table = events ColumnName = bytedata ColumnType = BLOB Default Value = x'00000000' PRIMARY KEY SEQUENCE = 0
The above is for the database name eventsDB, the Version is 1, there are 2 tables android_metadata (required by Apps and automatically generated) and events(actually 3 tables as sqlite_master also exists and is required by SQLite).
The SQL used to create the table is also included for each of the listed tables.
For each table the columns are listed showing the column's name, it's type, it's default value, and it's sequence within the primary index (0 = not in the primary index).
To include logDatabaseInfo replace CommonSQLiteUtilities (or create new) class with the following (note this includes all the other utilities as per orginal answer):-
public class CommonSQLiteUtilities {
public static final boolean ERROR_CHECKING_ON = true;
public static final boolean ERROR_CHECKING_OFF = false;
// SQLite MASTER TABLE definitions
static final String SQLITE_MASTER = "sqlite_master";
static final String SM_TABLE_TYPE_COLUMN = "type";
static final String SM_NAME_COLUMN = "name";
static final String SM_TABLENAME_COLUMN = "tbl_name";
static final String SM_ROOTPAGE_COLUMN = "rootpage";
static final String SM_SQL_COLUMN = "sql";
static final String SM_TYPE_TABLE = "table";
static final String SM_TYPE_INDEX = "index";
static final String PRAGMA_STATEMENT = "PRAGMA ";
static final String PRAGMA_DATABASELIST = "database_list";
static final String PRAGMA_USERVERSION = "user_version";
static final String PRAGMA_ENCODING = "encoding";
static final String PRAGMA_FOREIGNKEYLIST = "foreign_key_list";
static final String PRAGMA_INDEXINFO = "index_info";
static final String PRAGMA_INDEXLIST = "index_list";
static final String PRAGMA_TABLEINFO = "table_info";
static final String PRAGMA_DBLIST_SEQ_COL = "seq";
static final String PRAGMA_DBLIST_NAME_COL = "name";
static final String PRAGMA_DBLIST_FILE_COL = "file";
static final String PRAGMA_TABLEINFO_CID_COL = "cid";
static final String PRAGMA_TABLEINFO_NAME_COl = "name";
static final String PRAGMA_TABLEINFO_TYPE_COL = "type";
static final String PRAGMA_TABLEINFO_NOTNULL_COL = "notnull";
static final String PRAGMA_TABLEINFO_DEFAULTVALUE_COL = "dflt_value";
static final String PRAGMA_TABLEINFO_PRIMARYKEY_COL = "pk";
static final String CSU_TAG = "SQLITE_CSU";
private CommonSQLiteUtilities() {}
/**
* Write Database information to the log;
* Information wrttien is:
* the database path, (will/should show connected databases)
* the version number (note! user version i.e. version coded in DBHelper),
* the tables in the database (includes android_metadata but not sqlite_master),
* the columns of the tables
* #param db The SQLite database to be interrogated
*/
public static void logDatabaseInfo(SQLiteDatabase db) {
// Issue PRAGMA database_list commnand
Cursor dblcsr = db.rawQuery(PRAGMA_STATEMENT + PRAGMA_DATABASELIST,null);
// Write databases to the log
while (dblcsr.moveToNext()) {
Log.d(CSU_TAG,"DatabaseList Row " + Integer.toString(dblcsr.getPosition() + 1) +
" Name=" + dblcsr.getString(dblcsr.getColumnIndex(PRAGMA_DBLIST_NAME_COL)) +
" File=" + dblcsr.getString(dblcsr.getColumnIndex(PRAGMA_DBLIST_FILE_COL))
);
}
dblcsr.close();
// Issue PRAGMA user_version to get the version and write to the log
//Note! to set user_version use execSQL not rawQuery
Cursor uvcsr = db.rawQuery(PRAGMA_STATEMENT + PRAGMA_USERVERSION,null);
while (uvcsr.moveToNext()) {
Log.d(CSU_TAG,"Database Version = " +
Integer.toString(uvcsr.getInt(uvcsr.getColumnIndex(PRAGMA_USERVERSION))));
}
uvcsr.close();
// Select all table entry rows from sqlite_master
Cursor tlcsr = db.rawQuery("SELECT * FROM " +
SQLITE_MASTER + " WHERE " +
SM_TABLE_TYPE_COLUMN + "='" + SM_TYPE_TABLE + "'"
,null);
// For each table write table information to the log
// (inner loop gets column info per table)
while (tlcsr.moveToNext()) {
String current_table = tlcsr.getString(tlcsr.getColumnIndex(SM_TABLENAME_COLUMN));
Log.d(CSU_TAG,
"Table Name = " + current_table +
" Created Using = " + tlcsr.getString(tlcsr.getColumnIndex(SM_SQL_COLUMN)),
null
);
// Issue PRAGMA tabel_info for the current table
Cursor ticsr = db.rawQuery(PRAGMA_STATEMENT + PRAGMA_TABLEINFO +
"(" + current_table + ")",
null
);
// Write column info (see headings below) to the log
while (ticsr.moveToNext()) {
Log.d(CSU_TAG,"Table = " +
current_table +
" ColumnName = " +
ticsr.getString(ticsr.getColumnIndex(PRAGMA_TABLEINFO_NAME_COl)) +
" ColumnType = " +
ticsr.getString(ticsr.getColumnIndex(PRAGMA_TABLEINFO_TYPE_COL)) +
" Default Value = " +
ticsr.getString(ticsr.getColumnIndex(PRAGMA_TABLEINFO_DEFAULTVALUE_COL)) +
" PRIMARY KEY SEQUENCE = " + Integer.toString(
ticsr.getInt(ticsr.getColumnIndex(PRAGMA_TABLEINFO_PRIMARYKEY_COL))
)
);
}
ticsr.close();
}
tlcsr.close();
}
/**
* Generic get all rows from an SQlite table,
* allowing the existence of the table to be checked and also
* allowing the ROWID to be added AS a supplied string
*
* #param db The SQLiteDatabase
* #param tablename The name of the table from which the
* returned cursor will be created from;
* Note!
* #param use_error_checking Whether ot not to try to detect errors
* currently just table doesn't exist,
* true to turn on, false to turn off
* ERROR_CHECKING_ON = true
* ERROR_CHECKING_OFF = false
* #param forceRowidAs If length of string passed is 1 or greater
* then a column, as an alias of ROWID, will be
* added to the cursor
* #return the extracted cursor, or in the case of the
* underlying table not existing an empty cursor
* with no columns
*/
public static Cursor getAllRowsFromTable(SQLiteDatabase db,
String tablename,
boolean use_error_checking,
String forceRowidAs) {
String[] columns = null;
// Tablename must be at least 1 character in length
if (tablename.length() < 1) {
Log.d(CSU_TAG,new Object(){}.getClass().getEnclosingMethod().getName() +
" is finishing as the provided tablename is less than 1 character in length"
);
return new MatrixCursor(new String[]{});
}
// If use_error_checking is true then check that the table exists
// in the sqlite_master table
if (use_error_checking) {
Cursor chkcsr = db.query(SQLITE_MASTER,null,
SM_TABLE_TYPE_COLUMN + "=? AND "
+ SM_TABLENAME_COLUMN + "=?",
new String[]{SM_TYPE_TABLE,tablename},
null,null,null
);
// Ooops table is not in the Database so return an empty
// column-less cursor
if (chkcsr.getCount() < 1) {
Log.d(CSU_TAG,"Table " + tablename +
" was not located in the SQLite Database Master Table."
);
// return empty cursor with no columns
return new MatrixCursor(new String[]{});
}
chkcsr.close();
}
// If forcing an alias of ROWID then user ROWID AS ???, *
if(forceRowidAs != null && forceRowidAs.length() > 0) {
columns = new String[]{"rowid AS " +forceRowidAs,"*"};
}
// Finally return the Cursor but trap any exceptions
try {
return db.query(tablename, columns, null, null, null, null, null);
} catch (Exception e) {
Log.d(CSU_TAG,"Exception encountered but trapped when querying table " + tablename +
" Message was: \n" + e.getMessage());
Log.d(CSU_TAG,"Stacktrace was:");
e.printStackTrace();
return new MatrixCursor(new String[]{});
}
}
/**
* Create and return a Cursor devoid of any rows and columns
* Not used, prehaps of very little use.
* #param db The Sqlite database in which the cursor is to be created
* #return The empty Cursor
*/
private static Cursor getEmptyColumnLessCursor(SQLiteDatabase db) {
return new MatrixCursor(new String[]{});
}
/**
* Write column names in the passed Cursor to the log
* #param csr The Cursor to be inspected.
*/
public static void logCursorColumns(Cursor csr) {
Log.d(CSU_TAG,
new Object(){}.getClass().getEnclosingMethod().getName() +
" invoked. Cursor has the following " +
Integer.toString(csr.getColumnCount())+
" columns.");
int position = 0;
for (String column: csr.getColumnNames()) {
position++;
Log.d(CSU_TAG,"Column Name " +
Integer.toString(position) +
" is "
+ column
);
}
}
/**
* Write the contents of the Cursor to the log
* #param csr The Cursor that is to be displayed in the log
*/
public static void logCursorData(Cursor csr) {
int columncount = csr.getColumnCount();
int rowcount = csr.getCount();
int csrpos = csr.getPosition(); //<<< added 20171016
Log.d(CSU_TAG,
new Object(){}.getClass().getEnclosingMethod().getName() +
" Cursor has " +
Integer.toString(rowcount) +
" rows with " +
Integer.toString(columncount) + " columns."
);
csr.moveToPosition(-1); //Ensure that all rows are retrieved <<< added 20171016
while (csr.moveToNext()) {
String unobtainable = "unobtainable!";
String logstr = "Information for row " + Integer.toString(csr.getPosition() + 1) + " offset=" + Integer.toString(csr.getPosition());
for (int i=0; i < columncount;i++) {
logstr = logstr + "\n\tFor Column " + csr.getColumnName(i);
switch (csr.getType(i)) {
case Cursor.FIELD_TYPE_NULL:
logstr = logstr + " Type is NULL";
break;
case Cursor.FIELD_TYPE_FLOAT:
logstr = logstr + "Type is FLOAT";
break;
case Cursor.FIELD_TYPE_INTEGER:
logstr = logstr + " Type is INTEGER";
break;
case Cursor.FIELD_TYPE_STRING:
logstr = logstr + " Type is STRING";
break;
case Cursor.FIELD_TYPE_BLOB:
logstr = logstr + " Type is BLOB";
break;
}
String strval_log = " value as String is ";
String lngval_log = " value as long is ";
String dblval_log = " value as double is ";
String blbval_log = "";
try {
strval_log = strval_log + csr.getString(i);
lngval_log = lngval_log + csr.getLong(i);
dblval_log = dblval_log + csr.getDouble(i);
} catch (Exception e) {
strval_log = strval_log + unobtainable;
lngval_log = lngval_log + unobtainable;
dblval_log = dblval_log + unobtainable;
try {
blbval_log = " value as blob is " +
getBytedata(csr.getBlob(i),24);
} catch (Exception e2) {
e2.printStackTrace();
}
}
logstr = logstr + strval_log + lngval_log + dblval_log + blbval_log;
}
Log.d(CSU_TAG,logstr);
}
csr.moveToPosition(csrpos); // restore cursor position <<< added 20171016
}
/**
* Return a hex string of the given byte array
* #param bytes The byte array to be converted to a hexadecimal string
* #param limit the maximum number of bytes;
* note returned string will be up to twice as long
* #return The byte array represented as a hexadecimal string
*/
private static String getBytedata(byte[] bytes, int limit) {
if (bytes.length < limit) {
return convertBytesToHex(bytes);
} else {
byte[] subset = new byte[limit];
System.arraycopy(bytes,0,subset,0,limit);
return convertBytesToHex(subset);
}
}
// HEX characters as a char array for use by convertBytesToHex
private final static char[] hexarray = "0123456789ABCDEF".toCharArray();
/**
* Return a hexadecimal string representation of the passed byte array
* #param bytes The byte array to be represented.
* #return The string representing the byte array as hexadecimal
*/
private static String convertBytesToHex(byte[] bytes) {
char[] hexstr = new char[bytes.length * 2];
for (int i=0; i < bytes.length; i++) {
int h = bytes[i] & 0xFF;
hexstr[i * 2] = hexarray[h >>> 4];
hexstr[i * 2 + 1] = hexarray[h & 0xF];
}
return new String(hexstr);
}
}

Related

Exception: (Index -1 requested, with a size of 2) while retrieving data from a Cursor

Hey I have a problem with accessing the data from a Cursor in Android Studio.
I have two tables in the database events and messages. So each event in the events table may have multiple messages in the messages table. The event and its messages share a common event_id.
I am using LoaderCallbacks to get a Cursor from a ContentProvider. In the ContentProvider query method, I am using a rawQuery to INNER JOIN these two tables.
In this example I query for an event which shares its event_id with two messages from the message table. In the ContentProvider the rawQuery() method with the INNER JOIN was performed.
At this state I can see in the Debugger that the variable mCount from the Cursor is -1. I am not sure what this means but anyways, back in the onLoadFinish() method the Cursor now contains a variable mCursor and this variable again contains a variable mCount which is 2. Which makes sense, since the query should return a Cursor with two rows and since there are two messages. But now I try to get a String with the getString() method and the CursorIndexOutOfBoundsException: Index -1 requested, with a size of 2 is thrown.
I realy don't understand what this means. And I don't find any hint in die Cursor docs, what it means when mCount is -1.
Furthermore in the onLoadFinished method the variable mEditTable is null. And in other queries where I don't join anything it always contains a table.
I am quite sure that the issue somwhere lays with the INNER JOIN, since everything works fine if I use just a query method inside the ContentProvider.query method. But I don't know what I should do differently. I hope you might help me with this. I am aware that, if there is no message related to an event, this query will return an empty cursor with mCount = 0 and then again an exception will be thrown. But this is not the case in this example.
EventProvider class:
public class EventProvider extends ContentProvider {
private static final String LOG_TAG = EventProvider.class.getSimpleName();
private static final int EVENTS = 100;
private static final int EVENT_ID = 101;
private static final int EVENT_MESSAGE_ID = 102;
private static UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private EventDbHelper mEventDbHelper;
static {
sUriMatcher.addURI(EventContract.CONTENT_AUTHORITY, EventContract.PATH_EVENTS, EVENTS);
sUriMatcher.addURI(EventContract.CONTENT_AUTHORITY, EventContract.PATH_EVENTS + "/#", EVENT_ID);
sUriMatcher.addURI(EventContract.CONTENT_AUTHORITY, EventContract.PATH_EVENT_AND_ITS_MESSAGES + "/#", EVENT_MESSAGE_ID);
}
#Override
public boolean onCreate() {
mEventDbHelper = new EventDbHelper(getContext());
return true;
}
#Nullable
#Override
public Cursor query(#NonNull Uri uri, #Nullable String[] projection, #Nullable String selection, #Nullable String[] selectionArgs, #Nullable String sortOrder) {
SQLiteDatabase database = mEventDbHelper.getReadableDatabase();
Cursor retCursor;
final int match = sUriMatcher.match(uri);
switch (match) {
case EVENTS:
retCursor = database.query(EventEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
break;
case EVENT_ID:
selection = EventEntry.COLUMN_EVENT_ID + "=?";
selectionArgs = new String[] {String.valueOf(ContentUris.parseId(uri))};
retCursor = database.query(EventEntry.TABLE_NAME, projection,selection, selectionArgs, null, null, sortOrder);
break;
case EVENT_MESSAGE_ID:
selection = MessageEntry.TABLE_NAME + "." + MessageEntry.COLUMN_EVENT_ID + "=?";
selectionArgs = new String[] {String.valueOf(ContentUris.parseId(uri))};
StringBuilder sqlQuery = new StringBuilder();
sqlQuery.append("SELECT ");
if(projection != null && projection.length > 0) {
for(String s: projection) {
sqlQuery.append(s).append(", ");
}
}
sqlQuery.replace(sqlQuery.length()-2, sqlQuery.length(), " FROM ").append(MessageEntry.TABLE_NAME);
sqlQuery.append(" INNER JOIN ").append(EventEntry.TABLE_NAME).append(" ON ");
sqlQuery.append(EventEntry.TABLE_NAME).append(".").append(EventEntry.COLUMN_EVENT_ID).append("=");
sqlQuery.append(MessageEntry.TABLE_NAME).append(".").append(MessageEntry.COLUMN_EVENT_ID);
sqlQuery.append(" WHERE ").append(selection).append(" ORDER BY ").append(sortOrder).append(";");
Log.v(LOG_TAG, "SQL command for: " + uri.toString());
Log.v(LOG_TAG, sqlQuery.toString());
retCursor = database.rawQuery(sqlQuery.toString(), selectionArgs);
break;
default:
throw new IllegalArgumentException("Cannot query unknown URI: " + uri);
}
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
return retCursor;
}
EventActivity:
#NonNull
#Override
public Loader<Cursor> onCreateLoader(int id, #Nullable Bundle args) {
String[] projection = {
MessageEntry.TABLE_NAME + "." + MessageEntry.COLUMN_EVENT_ID,
MessageEntry.TABLE_NAME + "." + MessageEntry.COLUMN_SENDER,
MessageEntry.TABLE_NAME + "." + MessageEntry.COLUMN_DATE,
MessageEntry.TABLE_NAME + "." + MessageEntry.COLUMN_MESSAGE,
EventEntry.TABLE_NAME + "." + EventEntry._ID,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_NAME,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_DATE,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_DATE_ADDENDUM,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_CONTACT,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_STATUS,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_PICTURE_NAME,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_DESCRIPTION,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_STREET,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_HOUSE_NUMBER,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_POST_CODE,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_CITY,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_EMAIL,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_LOCATION,
EventEntry.TABLE_NAME + "." + EventEntry.COLUMN_SIGNED_UP
};
String sortOrder = MessageEntry.COLUMN_DATE + " DESC";
return new CursorLoader(this, mCurrentEventUri, projection, null, null, sortOrder);
}
#Override
public void onLoadFinished(#NonNull Loader<Cursor> loader, Cursor cursor) {
int indexName = cursor.getColumnIndex(EventEntry.COLUMN_NAME);
int indexImage = cursor.getColumnIndex(EventEntry.COLUMN_PICTURE_NAME);
String imageName = cursor.getString(indexImage); //Here the Exception is thrown
mEventName.setText(cursor.getString(indexName));
mEventAdapter.swapCursor(cursor);
}
If you need something else, I will upload it.
Thanks.
CursorIndexOutOfBoundsException: Index -1 requested, with a size of 2
-1 with Cursors will generally be 1 of 2 issues :-
that the column name passed to getColumnIndex is not a name of a column in the output.
that the Cursor is at the beginning that is at the position that is "before the first row", as is the case when a Cursor is returned.
I suspect that your issue is due to 2 as I believe the message indicates the row of the column.
2 can happen if a move???? (e.g. moveToFirst, moveToNext) method was not actioned (I cannot see any such move in your code) or that the result was not checked (the move methods return true or false to indicate whether or not the move request could be satisfied).
see (probably moveToFirst)
https://developer.android.com/reference/android/database/Cursor#move(int)
https://developer.android.com/reference/android/database/Cursor#moveToFirst()
https://developer.android.com/reference/android/database/Cursor#moveToLast()
https://developer.android.com/reference/android/database/Cursor#moveToNext()
https://developer.android.com/reference/android/database/Cursor#moveToPosition(int)
https://developer.android.com/reference/android/database/Cursor#moveToPrevious()
The fix would be to move the Cursor to a row, checking if the move was actually successful and to then extract the data.
e.g.
String imageName = "No Image"
if (cursor.moveToFirst() && indexImage > -1) {
imageName = cursor.getString(indexImage); //Here the Exception is thrown
}
The above, && indexImage > -1, would also weed out the situation where the column name does not exist in the Cursor.

Updating single row in SQLite Android Studio

So I'm trying to update one row of my sql database with this following method
public boolean modiService(String name, String price, String updatename, String updateprice ){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(COLUMN_NAME, updatename);
cv.put(COLUMN_RATE, updateprice);
db.update(TABLE_SERVICE, cv, COLUMN_NAME + " = ?" , new String[] {name});
return true;
}
However, whenever the function is called, it updates all the rows. I've tried to change the values within the "update" method called but I haven't been able to make it work
There is nothing intrinsically wrong with the code that you have shown. The example below, which utilises your exact code that you have shown, works.
As such your issue is either with other unprovided code or with the method you are using to determine that no data has been updated.
For this test the following was the code for the class that is subclass of SQLiteOpenHelper, in this case ServiceDBHelper.java :-
public class ServiceDBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "service.db";
public static final int DBVERSION = 1;
public static final String TABLE_SERVICE = "service";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_RATE = "rate";
public ServiceDBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
String crt_sql = "CREATE TABLE IF NOT EXISTS " + TABLE_SERVICE + "(" +
COLUMN_NAME + " TEXT PRIMARY KEY, " +
COLUMN_RATE + " TEXT" +
")";
db.execSQL(crt_sql);
}
#Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
}
public long insertService(String name, String price) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(COLUMN_NAME,name);
cv.put(COLUMN_RATE,price);
return db.insert(TABLE_SERVICE,null,cv);
}
public boolean modiService(String name, String price, String updatename, String updateprice ){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(COLUMN_NAME, updatename);
cv.put(COLUMN_RATE, updateprice);
db.update(TABLE_SERVICE, cv, COLUMN_NAME + " = ?" , new String[] {name});
return true;
}
public void logService(String description) {
String TAG = "LOGSERVICE";
Log.d(TAG,"Logging information for the Service Table for " + description);
SQLiteDatabase db = this.getWritableDatabase();
Cursor csr = db.query(TABLE_SERVICE,null,null,null,null,null,null);
Log.d(TAG,"Number of rows in the " + TABLE_SERVICE + " table is " + String.valueOf(csr.getCount()));
while (csr.moveToNext()) {
Log.d(TAG,
"Column " + COLUMN_NAME + " has a value of " + csr.getString(csr.getColumnIndex(COLUMN_NAME)) +
". Column " + COLUMN_RATE + " has a value of " + csr.getString(csr.getColumnIndex(COLUMN_RATE))
);
}
csr.close();
}
}
As you can see the modiService method is as per you code.
Other code has been added to :-
Create the table (named service) when the database is created.
Insert rows into the table to add some test data.
Write to data from the table to the log.
The following is the code used in an Activity to
- insert some rows,
- display the rows
- update (modify a row)
- display the rows
The code used in MainActivity.java was :-
public class MainActivity extends AppCompatActivity {
ServiceDBHelper mDBHlpr;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDBHlpr = new ServiceDBHelper(this);
mDBHlpr.insertService("Fred","125.45");
mDBHlpr.insertService("Mary","99.75");
mDBHlpr.insertService("Harry","245.34");
mDBHlpr.logService("After initial inserts");
mDBHlpr.modiService("Mary","99.75","Susan","333.33");
mDBHlpr.logService("After Updating Mary to Susan");
}
}
The relevant output in the Log was :-
11-18 06:51:02.303 1212-1212/so53291104.so53291104 D/LOGSERVICE: Logging information for the Service Table for After initial inserts
11-18 06:51:02.303 1212-1212/so53291104.so53291104 D/LOGSERVICE: Number of rows in the service table is 3
11-18 06:51:02.303 1212-1212/so53291104.so53291104 D/LOGSERVICE: Column name has a value of Fred. Column rate has a value of 125.45
11-18 06:51:02.303 1212-1212/so53291104.so53291104 D/LOGSERVICE: Column name has a value of Mary. Column rate has a value of 99.75
11-18 06:51:02.303 1212-1212/so53291104.so53291104 D/LOGSERVICE: Column name has a value of Harry. Column rate has a value of 245.34
11-18 06:51:02.307 1212-1212/so53291104.so53291104 D/LOGSERVICE: Logging information for the Service Table for After Updating Mary to Susan
11-18 06:51:02.307 1212-1212/so53291104.so53291104 D/LOGSERVICE: Number of rows in the service table is 3
11-18 06:51:02.307 1212-1212/so53291104.so53291104 D/LOGSERVICE: Column name has a value of Fred. Column rate has a value of 125.45
11-18 06:51:02.307 1212-1212/so53291104.so53291104 D/LOGSERVICE: Column name has a value of Susan. Column rate has a value of 333.33
11-18 06:51:02.307 1212-1212/so53291104.so53291104 D/LOGSERVICE: Column name has a value of Harry. Column rate has a value of 245.34
As can be seen the row Mary 99.75 has been changed using the modiService method to Susan 333.33.

Postgres jdbc PreparedStatement setObject does not cast values?

I am trying to insert the data onto different databases. Used dbutils to incorporate QueryRunner.batch() for batch inserts. This worked for SQL Server which casts the data to the corresponding type. I tried the same with PostgreSQL but of no avail. I tried a sample insert but that has failed in PostGres which validates my claim whereas the inserts for SQL Server were successful:
private void postGrestest() throws ClassNotFoundException, SQLException
{
Class.forName("org.postgresql.Driver");
String dropStmt = "DROP TABLE PUBLIC.TEST";
String createStmt = "CREATE TABLE PUBLIC.TEST(COL1 VARCHAR(10), COL2 BOOLEAN)";
String insertStmt = "INSERT INTO PUBLIC.TEST(COL1, COL2) VALUES (?, ?)";
try (Connection connection = DriverManager.getConnection(
"jdbc:postgresql://<host>:5432/<dbname>", "<username>", "<password>");
Statement stmt = connection.createStatement();
PreparedStatement ps = connection.prepareStatement(insertStmt);)
{
//stmt.execute(dropStmt);
stmt.execute(createStmt);
Random r = new Random();
for (int i = 0; i < 100; i++)
{
Object str = "Test" + i;
ps.setObject(1, str);
Object obj = String.valueOf(r.nextBoolean());
ps.setObject(2, obj);
ps.executeUpdate();
}
}
}
Exception in thread "main" org.postgresql.util.PSQLException: ERROR:
column "col2" is of type boolean but expression is of type character
varying Hint: You will need to rewrite or cast the expression.
Position: 49
private void sqlserverTest() throws SQLException, ClassNotFoundException
{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String dropStmt = "DROP TABLE DBO.TEST";
String createStmt = "CREATE TABLE DBO.TEST(COL1 VARCHAR(10), COL2 BIT)";
String insertStmt = "INSERT INTO DBO.TEST(COL1, COL2) VALUES (?, ?)";
try (Connection connection = DriverManager.getConnection(
"jdbc:sqlserver://<host>:<port>", "<username>", "<password>");
Statement stmt = connection.createStatement();
PreparedStatement ps = connection.prepareStatement(insertStmt);)
{
//stmt.execute(dropStmt);
stmt.execute(createStmt);
Random r = new Random();
for (int i = 0; i < 100; i++)
{
Object str = "Test" + i;
ps.setObject(1, str);
Object obj = String.valueOf(r.nextBoolean());
ps.setObject(2, obj);
ps.executeUpdate();
}
}
}
The reason I have to cast because, I read the data from a file and get as an object array. I do not have idea of the type beforehand and would like to use setObject instead of specific types setBoolean, setInteger, etc.
What other ways that I can use without casting at my application level and let the driver handle the cast?

“Table X has no column named Y” error inserting data into a SQLite database

I got this error:
10-08 22:23:06.635: E/SQLiteLog(20613): (1) table mytable has no column named GPS
10-08 22:23:06.645: E/SQLiteDatabase(20613): Error inserting AppName=Ster-Kinekor GPS=false Time=22:23:6 Network=true Date=2014/10/08
10-08 22:23:06.645: E/SQLiteDatabase(20613): android.database.sqlite.SQLiteException: table mytable has no column named GPS (code 1): , while compiling: INSERT INTO mytable(AppName,GPS,Time,Network,Date) VALUES (?,?,?,?,?)
I have checked for all the normal silly mistakes people make to get this error, pretty sure I don't have a comma or spacing wrong. Please help me find the error, really appreciate any assistance
Here's the relevant code:
public static final String KEY_ROWID = "id";
public static final String KEY_AppName = "AppName";
public static final String KEY_Date = "Date";
public static final String KEY_Time = "Time";
public static final String KEY_Gps = "GPS";
public static final String KEY_Network = "Network";
...
...
...
public void onCreate(SQLiteDatabase arg0) {
try{
arg0.execSQL("create table if not exists mytable ("
+ "id integer primary key autoincrement, "
+ KEY_AppName+" text not null,"
+ KEY_Date+" text,"
+ KEY_Time+ " text,"
+ KEY_Gps+" text,"
+ KEY_Network+" text"
+");");
} catch (Exception e){
e.printStackTrace();
}
}
public void insertRecord(LocationUsageDB lb)
{
SQLiteDatabase db = this.getWritableDatabase();
ContentValues initialValues = new ContentValues();//
initialValues.put(KEY_AppName, lb.AppName); //just do the same for any other columns
initialValues.put(KEY_Date, lb.Date);
initialValues.put(KEY_Time, lb.Time);
initialValues.put(KEY_Gps, lb.Gps);
initialValues.put(KEY_Network, lb.Network);
db.insert(TABLE_NAME, null, initialValues);
db.close();
}
And I send values here:
mydb.insertRecord(new LocationUsageDB(foregroundTaskAppName, date, t.hour+":"+t.minute+":"+t.second, "false", "true" ));

Android SQLite cannot bind argument

I'm trying to do a simple query of my database, where a unique Identification number is stored for a PendingIntent. To allow me to cancel a notification set by AlarmManager if needed.
The insertion of a value works fine, but I am unable to overcome the error:
java.lang.IllegalArgumentException: Cannot bind argument at index 1 because the index is out of range. The statement has 0 parameters.
Database structure:
public class DBAdapter {
private static final String TAG = "DBAdapter";
public static final String KEY_ROWID = "_id";
public static final String TASK = "task";
public static final String NOTIFICATION = "notification";
public static final int COL_ROWID = 0;
public static final int COL_TASK = 1;
public static final int COL_NOTIFICATION = 2;
public static final String[] ALL_KEYS = new String[] {KEY_ROWID, TASK, NOTIFICATION};
// DB info: it's name, and the table.
public static final String DATABASE_NAME = "TaskDB";
public static final String DATABASE_TABLE = "CurrentTasks";
public static final int DATABASE_VERSION = 3;
private static final String DATABASE_CREATE_SQL =
"create table " + DATABASE_TABLE
+ " (" + KEY_ROWID + " integer primary key, "
+ TASK + " text not null, "
+ NOTIFICATION + " integer"
+ ");";
Now I have created a method to extract the notification ID from the database as needed, using the following code:
public int getNotifID(long notifID){
String[] x = {ALL_KEYS[2]};
String[]args = new String[]{NOTIFICATION};
String where = NOTIFICATION + "=" + notifID;
int y = 0;
//String select = "SELECT "+NOTIFICATION+" FROM "+DATABASE_TABLE+" WHERE "+notifID+"="+NOTIFICATION;
//Cursor c = db.rawQuery(select,new String[]{});
Cursor c = db.query(true,DATABASE_TABLE,x,Long.toString(notifID),args,null,null,null,null,null);
if (c!= null && c.moveToFirst()){
y = c.getInt(COL_NOTIFICATION);
}
return y;
}
As you can see I have attempted to do this both with a rawQuery and a regular query, but with no success.
Rewrite your raw query to:
String select = "SELECT "+NOTIFICATION+" FROM "+DATABASE_TABLE+" WHERE "+NOTIFICATION+"=?";
Cursor c = db.rawQuery(select,new String[]{""+notifId});
if(c!=null && c.getCount()>0) {
c.moveToFirst();
}
c.close();