I was curious if androids SQLiteDatabase insert method automatically handles type conversion.
Here is my example:
I have a csv file with a column name of age. Its type will be an INTEGER.
Lets say I have already created the database and table.
Now I am parsing the csv file with CSVReader, which parses each line and inserts each value into an index of a String[].
In order to insert each line of data into the database, I have to use a ContentValue object, which allows me to store values in it.
//Parse each line and store in line...
ContentValue values = new ContentValue();
values.put(KEY_AGE, line[1]); // Assume line[1] is the age
database.insert(table, null, values);
If I store the age value as a string (as seen above), and then insert it into the table, does Android handle the conversion to INTEGER before inserting it into the database?
I am asking this because I am trying to insert a bunch of tables into a database, and it looks much cleaner when I can just iterate through an array then explicitly state each put call, i.e:
Also if anyone has any design suggestions feel free to tell me.
CLEAN
int i = 0;
for(String s : TransitContract.Routes.COLUMN_ARRAY) {
values.put(s, line[i]);
i++;
}
UGLY
values.put(TransitContract.Routes.KEY_ROUTE_ID, line[0]);
values.put(TransitContract.Routes.KEY_AGENCY_ID, line[1]);
values.put(TransitContract.Routes.KEY_SHORT_NAME, line[2]);
values.put(TransitContract.Routes.KEY_LONG_NAME, line[3]);
values.put(TransitContract.Routes.KEY_DESCRIPTION, line[4]);
values.put(TransitContract.Routes.KEY_ROUTE_TYPE, Integer.parseInt(line[5]));
values.put(TransitContract.Routes.KEY_URL, line[6]);
values.put(TransitContract.Routes.KEY_COLOR, line[7]);
values.put(TransitContract.Routes.KEY_TEXT_COLOR, line[8]);
return mDatabase.insert(TransitContract.Routes.TABLE_NAME, null, values);
When you declare a column as INTEGER, SQLite will automatically convert strings to numbers, if possible.
See the documentation for Type Affinity.
If your ContentProvider doesn't restrict it (i.e. pass it directly to the SQLiteDatabase.insert() method), it should work. SQLite is not that picky about the types used in queries/inserts and the actual column type.
However, it would be best practice to parse and check the values before inserting. Otherwise you might actually insert a string which can't be parsed as integer and therefore retrieving the value might fail.
References:
Boolean datatype accepting string value and integer value
SQLite table with integer column stores string
Related
Mistakenly I have put a double value divided by zero in an SQLite database column.
In "DB Browser", it looks like string "Inf" or "inf". However, "WHERE MY_COLUMN = 'inf'" gives no result.
How can I retrieve all records containing this value via an SQL query?
Also, how can I identify this value in the cursor loop?
I have tried these:
cursor.getString(cursor.getColumnIndex(MY_COLUMN)).equalsIgnoreCase("inf")
cursor.getDouble(cursor.getColumnIndex(MY_COLUMN)) == Double.POSITIVE_INFINITY
No result as well.
I am creating a table using the following query:
private static final String SQL_CREATE_ENTRIES = "CREATE TABLE person_info ( uniqueId INTEGER,first_name TEXT,last_name TEXT,
address TEXT)";
sqLiteDatabase.execSQL(SQL_CREATE_ENTRIES);
I am inserting the values as follows:
// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put("first_name", "Anshul");
values.put("last_name", "Jain");
values.put("address", "Bangalore");
return db.insert(TABLE_NAME, null, values);
Note that I am not giving any value for uniqueId column. Thus, uniqueId column values are null.
When I query the database and try to the type of each column using cursor.getType(i), it returns Cursor.FIELD_TYPE_NULL for uniqueId column. According to the documentation, if all the column values are null, then it will return this value. But ideally it should return Cursor.FIELD_TYPE_INTEGER because that's what I declared while creating the database.
Is there any other way of retrieving the correct column type when all the values of a column are null.
Most SQL database engines (every SQL database engine other than
SQLite, as far as we know) uses static, rigid typing. With static
typing, the datatype of a value is determined by its container - the
particular column in which the value is stored.
SQLite uses a more general dynamic type system. In SQLite, the
datatype of a value is associated with the value itself, not with its
container.
SQLite Docs
This behavior of returning Cursor.FIELD_TYPE_NULL(which according to you is not ideal) is absolutely ideal because SQLite is designed in that way only.
Querying the database to get the type of a Container using cursor.getType(i) will only work if the Container is not NULL otherwise it returns Cursor.FIELD_TYPE_NULL (as in your case).
You can use PRAGMA table_info(table_name) for retrieving the datatype.
Check this SO answer -- Getting the type of a column in SQLite
I have a sqlite database where all rows have a UUID as the primary key, the db column is defined as a BLOB.
The keys are inserted as byte[] instead of Strings to avoid wasting storage and index spaces. I can insert, update and delete rows using SQLiteDatabase.compileStatement and using bindBlob on the SQLiteStatement but I can't find anyway to bind a blob to the parameters of a SELECT query.
Both SQLiteDatabase.query and .rawQuery expects my WHERE arguments to be Strings which will never match my byte array blobs. I can find my row if I construct my WHERE manually using a BLOB literal like this:
final Cursor query = db.query(getTableName(),
getColumns(),
"id = X'" + bytesToHex(getByteArrayFromUUID(id)) + "'" ,
null,
null,
null,
null);
But then I am vulnerable to SQL injections...
In every other language I have used SQLite in this is not a problem, is there really no way to get a standard prepared SELECT statement with android SQLite?
Most of the APIs in Android sqlite expect the parameter to be strings.
You can use compileStatement() to prepare your query and then use bindBlob() to bind a blob argument to it. Getting useful results out from the query is not easy though, SQLiteStatement has methods for only a few 1x1 result sets.
On the other hand, using a blob as a key doesn't seem like a good idea.
I have an Android SQLite table definition like this:
create table MyTable(..., myDate integer, ...);
Later in my code I query this table to retrieve the value for myDate via a cursor:
long dateInstant = cursor.getLong(cursor.getColumnIndexOrThrow("myDate"));
However, I'm wondering if this is the best approach. If the value is not set (i.e. null on the database) the above method returns 0, which happens to also be a valid value. I need to differentiate valid values from null values. Also, from the javadoc for getLong:
The result and whether this method throws an exception when the column value is null, the column type is not an integral type, or the integer value is outside the range [Long.MIN_VALUE, Long.MAX_VALUE] is implementation-defined.
which seems to suggest, if I read this correctly, that at some point in the future the SQLite implementation shipped in Android may choose to throw an exception rather than return 0. I'm looking for a consistent solution across Android versions.
In short, how can I robustly handle the retrieval of integer data from a column in a way which allows me to differentiate between valid values and null?
You can do
long dateInstant = 0; // some value to represent null
if (!cursor.isNull (colIndex))
dateInstance = cursor.getLong (colIndex);
I've accidentally inserted the string datetime() into my database (in stead of the result of the function), and never saw my mistake until i did a listing of the data. The string "datetime()" is in the date field of all the records, yet the field type is datetime!
Is this normal for the sqllite that is on Android?? What's the point of field types then?
Yes, sqlite isn't strongly statically typed like most databases. When you assign a type to a column, it's more like a suggestion about how you'd want the value stored. If you create a column which is a text column, it will store numbers you put there as strings, since it can convert a number to a string for you. If you create a column with a integer type and try to store a string in it, it stores a string if it can't be converted to an integer. Read more about it here