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.
Related
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.
How to check if data queried from android sqlite database is empty in a bindView method?
This is what I have done so far, but I think I am doing the wrong thing.
UPDATE
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout);
SQLiteDatabase db;
db = openOrCreateDatabase(
"no.db"
, SQLiteDatabase.CREATE_IF_NECESSARY
, null
);
//CREATE TABLES AND INSERT MESSAGES INTO THE TABLES
String CREATE_TABLE_NOTICES = "CREATE TABLE IF NOT EXISTS notices ("
+ "ID INTEGER primary key AUTOINCREMENT,"
+ "NOT TEXT,"
+ "DA TEXT)";
db.execSQL(CREATE_TABLE_NOTICES);
Cursor myCur = null;
// SQLiteDatabase db;
myCur = db.rawQuery("SELECT ID as _id,NOT,DA FROM notices order by ID desc", null);
mListAdapter = new MyAdapter(Page.this, myCur);
setListAdapter(mListAdapter);
}
#Override
public void bindView(View view, Context context, Cursor myCur) {
TextView firstLine=(TextView)view.findViewById(R.id.firstLine);
if(myCur==null){
String message="No Message";
firstLine.setText(message);
}
}
The keyword NOT is a reserved word in MySQL. I'd be very surprised if it's not a reserved word in SQLite.
I think the problem is here:
SELECT ID as _id,NOT,DA FROM notices order by ID desc
-- ^^^
It looks like the reserved word NOT is being used as a column name. To use that as an identifier, it will need to be "escaped", by enclosing it in backtick characters.
SELECT ID as _id,`NOT`,DA FROM notices ORDER BY ID DESC
-- ^ ^
That's the normative pattern in MySQL. For SQLite, you use the (MySQL compatible ) backtick characters, or you can use double quotes (more ANSI standard compliant.)
It's also possible to qualify the column name. (I'm certain this works in MySQL).
SELECT n.ID AS `_id`, n.NOT, n.DA FROM notices n ORDER BY n.id DESC
-- ^^ ^^ ^^ ^ ^^
References:
MySQL Reference Manual: 9.3 Keywords and Reserved Words https://dev.mysql.com/doc/refman/5.5/en/keywords.html
Certain keywords, such as SELECT, DELETE, or BIGINT, are reserved and require special treatment for use as identifiers such as table and column names.
SQLite: SQLite Keywords https://www.sqlite.org/lang_keywords.html
If you want to use a keyword as a name, you need to quote it. There are four ways of quoting keywords in SQLite:
It may also be a problem in the CREATE TABLE statement. Since you are creating the table, you have the option of using a different name for the column, a name which is not a reserved word.
This is my database
db.execSQL("CREATE TABLE " + DATABASE_TABLE + " (" +
KEY_ROWID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
KEY_CATEGORY + " TEXT NOT NULL, " +
KEY_DATE + " TEXT, " +
KEY_PRICE + " LONG, " +
KEY_DETAILS + " TEXT NOT NULL);"
);
and this is the method for deleting all data
public void deleteall() {
// TODO Auto-generated method stub
ourDatabase.delete(DATABASE_TABLE, null, null);
}
and this is the method for deleting a particular data
public void deletentry(long l) {
// TODO Auto-generated method stub
ourDatabase.delete(DATABASE_TABLE, KEY_ROWID + " = " + l,null);
}
Here using the data I deleted but incremented row-id remains there, which I want to reset to 1 as the data is deleted also if I delete a particular data the row-id changes it's value in a sequence manner there should be no gap in between row-id.
I don't think autoincrement is your best option. If your goal is to create a simple numbered table with no gaps, it may be much easier to handle this using a table with a column for you to manually keep up with your ID's.
If you created a table like that, you could then write helper methods in your code for the following operations: addColumn, removeColumn, emptyTable.
Your addColumn method would query the table and determine the max(ID) then add 1 and use that number for the next entry.
Your removeColumn method could remove the entry by ID, then use that ID to resequence everything above it. Or, if order is not important, it could take the last row and re-id it to fill in the gap.
Your emptyTable method could remove all entries.
Update
Maybe this can get you started. The methods would need to be defined in your program. You would have to put the code inside them and then set them up to be called.
For example:
public void addColumn(String category, long date, String details) {
//code here would need to determine the max of ID and add one
//to it. the sql below would retrieve max, i dont know the sql lite code
//off the top of my head.
//SELECT MAX(ID) FROM DATABASE_TABLE;
int newID = max + 1;
//add row to the database using newID
}
public void removeColumn(int id) {
//remove column from database
//DELETE FROM DATABASE_TABLE WHERE ID = id;
//change last entry to use id
//UPDATE DATABASE_TABLE SET ID = id WHERE id = (SELECT MAX(ID) FROM DATABASE_TABLE);
}
public void emptyTable() {
//DELETE FROM DATABASE_TABLE;
}
To call these methods, you would call them like any other java method in your class:
addColumn(12, 'text', (long)100, 'text');
removeColumn(10);
emptyTable();
SQLite keeps the largest ROWID in the special SQLITE_SEQUENCE table. You can delete that table as:
db.delete("SQLITE_SEQUENCE","NAME = ?",new String[]{TABLE_NAME});
Unfortunately, SQLite AUTOINCREMENT is not guaranteed to work as you want. Quoting the docs:
If no ROWID is specified on the insert, or if the specified ROWID has
a value of NULL, then an appropriate ROWID is created automatically.
The usual algorithm is to give the newly created row a ROWID that is
one larger than the largest ROWID in the table prior to the insert. If
the table is initially empty, then a ROWID of 1 is used. If the
largest ROWID is equal to the largest possible integer
(9223372036854775807) then the database engine starts picking positive
candidate ROWIDs at random until it finds one that is not previously
used. If no unused ROWID can be found after a reasonable number of
attempts, the insert operation fails with an SQLITE_FULL error. If no
negative ROWID values are inserted explicitly, then automatically
generated ROWID values will always be greater than zero.
If you need this specific behaviour you described, the only solution would be for you to manually control the KEY_ROWID for your table, making sure you properly account for inserts and deletes.
You can also delete your table from SQLite_Sequence using
sqlDb.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = 'YOUR_TABLE_NAME'");
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.
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