SQLite - Is it possible to insert a BLOB via insert statement? - android

I'm developing an Android application and i'm using a Sqlite database to store some bitmaps. I want some images to be automatically inserted when the user installs the application.
I'm using the SQLiteOpenHelper class like this:
public class DatabaseHelper extends SQLiteOpenHelper {
...
DatabaseHelper(Context context, String nameOfDB, int version, String[] scriptSQLCreate,
String scriptSQLDelete) {
super(context, nameOfDB, null, version);
this.scriptSQLCreate = scriptSQLCreate;
this.scriptSQLDelete = scriptSQLDelete;
}
#Override
public void onCreate(SQLiteDatabase db) {
int numScripts = scriptSQLCreate.length;
for(int i = 0; i<numScripts; i++){
Log.i(TAG,"Creating database, executing script " + i);
db.execSQL(scriptSQLCreate[i]);
}
}
}
...
I want to pass a constant to the scriptSQLCreate parameter shown above that would be like so:
private static final String[] SCRIPT_DATABASE_CREATE = {
"create table memes( id integer primary key autoincrement," +
+ " img blob not null," +
+ " name text not null unique)" ,
"insert into memes(img,name) values(BITMAP1,'1.jpg')",
"insert into memes(img,name) values(BITMAP2,'2.jpg')",
"insert into memes(img,name) values(BITMAP3,'3.jpg')"}
}
Any help will be much apreciated,
Thx,
Tulio Zahn

If you really, really want to you can use a very long hex literal as a blob literal:
insert into memes(img, name) values(X'0102030405060708090a0b0c0d0e0f', '1.jpg')
However, this is usually a bad idea; instead, go look at parameterised queries. They will let you compile a statement once using placeholders instead of actual values, and then reuse it many times, filling in the placeholders as needed:
SQLiteStatement p = sqlite.compileStatement("insert into memes(img, name) values(?, ?)");
byte[] data = loadData("1.jpg");
p.bindBlob(1, data);
p.bindString(2, "1.jpg");
p.execute();
byte[] data = loadData("2.jpg");
p.bindBlob(1, data);
p.bindString(2, "2.jpg");
p.execute();
(Warning --- code not tested.)
In general you should be using parameterised queries everywhere, as they're a sure-fire way to avoid SQL injection attacks, plus are usually easier and clearer. Assembling SQL queries by glueing strings together should be avoided at all costs.

Your data table has some invisible word which you can not see. Check your db file with the db tools like navicat for sqlite. Please pay attention to the error word in the table.

Related

<column definition name> or <table constraint> expected, got 'Index'

I have got the error message " or expected, got 'Index'" when I was trying to create a table and I do not really understand why is the code expecting a column definition or table constraint at this line
I have tried with changing the whitespaces, however that only change the place where the error is prompted. The content of the error message does not change
This is the part that I have declared the strings
public class TaskEntry implements BaseColumns {
public static final String TABLE = "Users";
public static final String INDEX = "Index";
public static final String COL_TASK_TITLE = "title";
}
The following is my code for the creating table part
public void onCreate(SQLiteDatabase db) {
String createTable = "CREATE TABLE " + Item_contract.TaskEntry.TABLE + " ( " +
Item_contract.TaskEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
Item_contract.TaskEntry.INDEX + " INTEGER NOT NULL, " +
Item_contract.TaskEntry.COL_TASK_TITLE + " TEXT NOT NULL" + ");";
db.execSQL(createTable);
}
You cannot use INDEX as a column name as it is a keyword.
The SQL standard specifies a large number of keywords which may not be
used as the names of tables, indices, columns, databases, user-defined
functions, collations, virtual table modules, or any other named
object. The list of keywords is so long that few people can remember
them all. For most SQL code, your safest bet is to never use any
English language word as the name of a user-defined object.
SQL As Understood By SQLite - SQLite Keywords
So change
public static final String INDEX = "Index";
perhaps to
public static final String INDEX = "IX";
You could enclose the column name if you really wanted it to be INDEX e.g.
public static final String INDEX = "[Index]";
As per :-
If you want to use a keyword as a name, you need to quote it. There are four ways of quoting keywords in SQLite:
'keyword' A keyword in single quotes is a string literal.
"keyword" A keyword in double-quotes is an identifier.
[keyword] A keyword enclosed in square brackets is an identifier. This is not standard SQL. This quoting mechanism is used by MS Access and SQL Server and is included in SQLite for compatibility.
`keyword` A keyword enclosed in grave accents (ASCII code 96) is an identifier. This is not standard SQL. This quoting mechanism is used by MySQL and is included in SQLite for compatibility.
SQL As Understood By SQLite - SQLite Keywords
Note
You will have to do one of the following to get the onCreate method to run and thus alter the schema:-
Delete the App's data.
Uninstall the App.

SQLiteException: no such column:, while compiling

This is my db helper class, that creates a db when the app is installed the first time.
When I register a new user, if his name is letters like "john", it gives me an exception.
However, usernames like 4, 56 (i.e.: digits only) give no errors. Why?
class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context) {
super(context, "myDB", null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
Log.d("x", " database CREATED!!! -------------------------");
db.execSQL("create table userData ("
+ "id integer primary key autoincrement,"
+ "name text,"
+ "password text,"
+ "hero int,"
+ "level int,"
+ "loggedin int"
+ ");");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
EDIT:
This is the piece of code where I actually try to update my db.
public void login(View v){
//..some code here
db.execSQL("update userData set loggedin=1 where name=" + username2) ;
}
All strings in sql must be enclosed by '. Quoted from https://www.sqlite.org/lang_expr.html:
"A string constant is formed by enclosing the string in single quotes ('). A single quote within the string can be encoded by putting two single quotes in a row - as in Pascal. C-style escapes using the backslash character are not supported because they are not standard SQL."
if his name is letters like "john", it gives me an exception.
However, usernames like 4, 56 (i.e.: digits only) give no errors. Why?
Wild Guess: You forgot to enclose John in single quotes, i.e.: 'John'.
The reason for this being that John is a string.
And strings must be delimited by single quotes (')
Be aware that, if the string itself contains a quote (or apostrophe), i.e. 'I'm aware of that', then the apostrophe it has to be doubled, i.e. 'I''m aware of that'.
If you don't want to hassle with quotes, there's a better way to make Android handle it for you: bound parameters.
In practice, all the values in your query should be replaced by a question mark placeholder (?).
And you give the query a String array containing all the values to be replaced.
This way you are also protected against SQL injection.
An example would clarify the concept better:
// Two values to be passed...
final String sql =
"SELECT date, score FROM " + DB_TABLE +
" WHERE strftime('%Y', date) = ? AND " +
"CAST((strftime('%m', date)) AS INTEGER) = ? ORDER BY date DESC";
// ... into the string array parameter of the rawQuery() overload
final Cursor cur =
db.rawQuery
(
sql,
new String[]
{
String.valueOf(CLS_Utils.yearUsing),
String.valueOf(month)
}
);
Note.
The same technique applies to execSQL() as well.
Therefore it can be used on INSERTs, UPDATEs and DELETEs as well.

Android Database Writing error

I got an app that as an internal database, and the app crashes with this error :
04-30 20:46:30.836 1647-1647/prosis.guiatour E/SQLiteLog﹕ (1) no such column: Basílica_Santa_Luzia
and the code that this is refering to is :
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_QUERY);
Log.e("Database Operations"," Table Created....");
db.execSQL(addingInfo("Basílica_Santa_Luzia", "Stuff", "10.43597", "-10.5747"));
Log.e("Database Operations"," Data Inserted");
}
public String addingInfo(String nome, String cat, String lat, String longi){
String Query = "INSERT INTO "+ Table_name+" VALUES("+nome+","+cat+","+lat+","+longi+");";
return Query;
}
And this is my contructer query :
private static final String CREATE_QUERY = "CREATE TABLE "+ Contract.NewLocalInfo.Table_name+" ("+ Contract.NewLocalInfo.Nome+" TEXT,"+ Contract.NewLocalInfo.Categoria+" TEXT,"+ Contract.NewLocalInfo.Latitude+" TEXT,"+ Contract.NewLocalInfo.Longitude+" TEXT);";
I think the SQL is well Writen so what do you think is the problem here?
Your SQL would not be valid. Based on your function, you would get:
INSERT INTO table_name VALUES(Basilica_Santa_Luzia,Stuff,10.43,10.57)
which is not valid. You have to single-quote strings like:
INSERT INTO table_name VALUES('Basilica_Santa_Luzia','Stuff','10.43','10.57')
But, you should not be writing any SQL unless you have complex requirements. You should use what's built into Android or get a third-party option.
Writing an SQL Query string creates security risks and causes errors like this one.
To insert, instead do:
ContentValues cv = new ContentValues();
cv.put("key","value");
cv.put("key2","value2");
db.insert("table_name",cv);
This approach will automatically escape any paramaters and guarantee that your query is not erroneous.
Also, the latitude and longitude probably should not be Strings. I don't remember for sure, but what I think you need is double for the Java/Android side and in normal SQL, you would need a decimal datatype on the column, but its SQLite and I'm not sure what you're supposed to do for a decimal column in sqlite, so maybe Duck it.
Also, I made a database manager class which wraps the SQLiteOpenHelper class to ensure synchronization and thread safety, which can simplify your life a ton and prevent tons of errors.
Add single quote around all values then this insert will work. Final query should be like below
INSERT INTO TABLE_NAME VALUES('Basílica_Santa_Luzia','Stuff','10.43597','-10.5747');

How to detect if a field is primary key or not

My question is that it is possible to know a field is primary key of table by looking cursor. Or how can we learn which field is primary key?
I want write a class like that. So I must learn it programatically
public class BO {
private HashMap<String, Object> data;
private String primaryKey="";
public BO() {
data=new HashMap<String, Object>();
}
public String getUpdateQuery() {
return null;
}
public String getInsertQusery() {
return null;
}
public String getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(String primaryKey) {
this.primaryKey = primaryKey;
}
public int getLogicalReference() {
return logicalReference;
}
public void loadDataFromCursor(Cursor cur){
String[] fields=cur.getColumnNames();
for(int i=0;i<fields.length;i++){
switch (cur.getType(i)) {
case Cursor.FIELD_TYPE_INTEGER:
data.put(fields[i], cur.getInt(i));
break;
default:
break;
}
}
}
public void setLogicalReference(int logicalReference) {
this.logicalReference = logicalReference;
}
}
Just like CL. said in his comment, I believe a Cursor does not hold this information. Whether a column holds a primary key is related to the table structure, which is different from the content of the table. A Cursor is used to access data and not give information about the table's structure.
Besides, the Cursor interface in Android is not necessarily related to a SQLiteDatabase, so you should not be able to get this kind of information from it.
However, I can't think of any good usage case where this should be really useful. Maybe some kind of far-fetched genericity of the code; in any case, there is a good chance you should rethink your design (or explain it if you really think it's correct; in that case, I'm curious !)
Here is an example using Python and Mysql.
pkey = cursor.execute("SHOW KEYS FROM " + tablename + " WHERE Key_name = 'PRIMARY'")
And the Use Case :
Query = "INSERT INTO table " + str(tuple(table[0].keys()))
Query += "\nVALUES\n"
Query += str(tuple(table[0].values()))
Query += "\nON DUPLICATE KEY UPDATE\n"
for key in table[0].keys():
if not key in pkey:
Query += key + " = VALUES(" + key + "),\n"
Query = Query.rstrip(",")
cursor.execute(Query)
The resulting string would Insert or Update the table. If you try to push the Primary Key to the table you throw an error of Duplicate Primary. My program is parsing a directory of JSON files and importing them into database as part of an API retrieval process. I am pulling data from 3 different API's and would like to keep the code generic so that I can re-use it.
I am less familiar with SQLLite, however you could implement something that would do this for you, by naming your primary key with something that indicated it was a primary key : pk_id for example and then just parsing the name of the field to find if it contains pk.

onCreate not being called after getWritableDatabase/getReadableDatabase

My app's got a database with three tables in it: one to store the names of the people it tracks, one to track an ongoing event, and one - for lack of a better term - for settings.
I load the first table when the app starts. I ask for a readable database to load in members to display, and later I write to the database when the list changes. I've had no problems here.
The other two tables, however, I can't get to work. The code in the helper classes is identical with the exception of class names and column names, and (at least until the point where I try to access the table) the code to use the table is nearly identical as well.
Here's the code for my helper class (I've got a separate helper for each table, and as I said, it's identical except for class names and columns):
public class db_MembersOpenHelper extends SQLiteOpenHelper
{
public static final String TABLE_NAME = "members_table";
public static final String[] COLUMN_NAMES = new String[] {
Constants.KEY_ID,
"name",
"score"
};
private static final String TABLE_CREATE = "CREATE TABLE " + TABLE_NAME + " ("
+ COLUMN_NAMES[0] + " INTEGER PRIMARY KEY autoincrement, "
+ COLUMN_NAMES[1] + " TEXT, "
+ COLUMN_NAMES[2] + " INTEGER);";
public db_MembersOpenHelper(Context context)
{
super(context, Constants.DATABASE_NAME, null, Constants.DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) { db.execSQL(TABLE_CREATE); }
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.w("TaskDBAdapter", "Upgrading from version " + oldVersion + " to " + newVersion + ".");
// Do nothing. We do not have any updated DB version
}
}
Here's how I use it successfully:
db_MembersOpenHelper membersDbHelper = new db_MembersOpenHelper(this);
SQLiteDatabase membersDb = membersDbHelper.getReadableDatabase();
Cursor membersResult = membersDb.query(TABLE_NAME, null, null, null, null, null, null);
members = new HashMap<String, Integer>();
membersResult.moveToFirst();
for(int r = 0; r < membersResult.getCount(); r++)
{
members.put(membersResult.getString(1), membersResult.getInt(2));
membersResult.moveToNext();
}
membersDb.close();
And here's where it fails:
db_PlayersOpenHelper playersDbHelper = new db_PlayersOpenHelper(this);
final SQLiteDatabase playersDb = playersDbHelper.getWritableDatabase();
if(newGame)
{
for(String name : players)
{
ContentValues row = new ContentValues();
row.put(COLUMN_NAMES[1], name);
row.put(COLUMN_NAMES[2], (Integer)null);
playersDb.insert(TABLE_NAME, null, row);
}
}
The first one works like a charm. The second results in ERROR/Database(6739): Error inserting achievement_id=null name=c
android.database.sqlite.SQLiteException: no such table: players_table: , while compiling: INSERT INTO players_table(achievement_id, name) VALUES(?, ?);
...
I did do some testing, and the onCreate method is not being called at all for the tables that aren't working. Which would explain why my phone thinks the table doesn't exist, but I don't know why the method isn't getting called.
I can't figure this out; what am I doing so wrong with the one table that I accidentally did right with the other?
I think the problem is that you are managing three tables with with three helpers, but only using one database. SQLiteOpenHelper manages on database, not one table. For example, it checks to see whether the database, not table, exists when it starts. It already does, so onCreate() does not fire.
I would manage all tables with one helper.
Let me see if I get this right. You are trying to create one database with three tables. But when you create the database, you create just one table; you are somehow instantiating the same database at a different place and wonder why its onCreate method doesn't get called. Is this a correct interpretation?
My strategy would be to try and create all three tables in the single onCreate() method.
If you are working with multiple tables, then you have to create all of the tables at once. If you have run your application first and later you update your database, then it will not upgrade your DB.
Now delete your application, then run it again.
There is one more solution but it is not proper. You can declare onOpen method in which you can call onCreate. And add IF NOT EXISTS before table name in your create table string. – Sourabh just now edit

Categories

Resources