Android SQLite queries marked as Error by Android Studio - android

In one of my Android apps I use some SQLite queries that are starting to give problems since I've updated to Android Studio 3.0.
Despite I can compile and run the app they are marked as error.
These are the queries.
db.execSQL("INSERT INTO table1 (…) ... substr(replace(table2.field2, ',', ''), table1.field1, 1) … FROM table1 … WHERE …");
gives error in 'replace':
')' or expression expected, got 'replace'
I think this would work because replace returns a String.
And:
db.execSQL("DELETE FROM table1 WHERE table1.rowid IN (…)");
gives error in 'rowid':
column name or table name expected, got 'rowid'
I think this would work too because SQLite adds an implicit rowid to each table.
And:
db.execSQL("ATTACH DATABASE ? as newDB", new String[] { getDatabasePath(DATABASE_NAME_NEW).getPath() });
gives error in '?':
expression expected, got '?'
I've changed this query to this:
db.execSQL("ATTACH DATABASE '"+getDatabasePath(DATABASE_NAME_NEW).getPath()+"' as newDB");
Also:
db.execSQL("UPDATE table1 SET field1=0 WHERE field2 IN
(SELECT field2 FROM table2 WHERE field3 LIKE '%blablabla%' OR field4 LIKE '%blebleble%' OR (field5 LIKE '%bliblibli%' AND field6 NOT LIKE '%blobloblo%')
COLLATE NOCASE)");
Which gives these errors:
In 'field6 NOT LIKE':
')' or '.' expected, got 'NOT'
In 'NOCASE)':
BETWEEN, IN or semicolon expected, got ')'
I don't know what is wrong with this one.
Are these queries correct? In iOS are identical and they are working well.
UPDATE:
These are the full queries (unfortunately due to my client's security policy I have to change the name of the tables and fields, but I hope is ok to detect the errors).
db.execSQL("INSERT INTO table1 (field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14) " +
"SELECT t6.field1, t7.field2, t3.field4, t3.field5, CASE WHEN t5.field1='1' THEN '11' " +
"WHEN t5.field1='2' THEN '22' WHEN t5.field1='3' THEN '33' WHEN t5.field1='4' THEN '44' " +
"ELSE t5.field1 END AS newfield1, t4.field1, CASE WHEN t2.field1 = '0' THEN t4.field2 " +
"ELSE substr(replace(t4.field3, ',', ''), t2.field1, 1) END AS newfield2, t2.field4, '0', -1, '0', '1', -1, t8.field1 " +
"FROM table2 AS t2, table3 AS t3, table4 AS t4, table5 AS t5, table6 AS t6, table7 AS t7, table8 AS t8 " +
"WHERE t2.field2=t3.field3 AND t2.field3=t4.field1 AND t2.field3=t5.field2 AND t3.field2=t7.field2 AND " +
"t3.field1=t6.field1 AND substr(t5.field1, 1, 1) IN ('1', '2', '3', '4') AND substr(t5.field1, 2, 1) IN ('1', '2', '3', '4') AND t2.field5=t8.field2");
db.execSQL("DELETE FROM table1 WHERE table1.rowid IN (SELECT table1.rowid FROM table1, table4 AS t4 WHERE table1.field6=t4.field1 AND (((t4.field2 NOT LIKE '%%' || substr(table1.field5, 1,1) || '%%') AND (t4.field3 NOT LIKE '%%' || substr(table1.field5, 1,1) || '%%')) OR ((t4.field2 NOT LIKE '%%' || substr(table1.field5, 2,1) || '%%') AND (t4.field3 NOT LIKE '%%' || substr(table1.field5, 2,1) || '%%'))))");
db.execSQL("ATTACH DATABASE ? as newDB", new String[] { getDatabasePath(DATABASE_NAME_NEW).getPath() });
Changed to:
db.execSQL("ATTACH DATABASE '"+getDatabasePath(DATABASE_NAME_NEW).getPath()+"' as newDB");
And the error is gone.
db.execSQL("UPDATE table1 SET field13=1 WHERE field2 IN (SELECT t7.field2 FROM table7 AS t7 WHERE t7.field1='Text1' OR t7.field1='Text2' OR t7.field1 LIKE '%TEXT3%' OR t7.field1 LIKE '%TEXT4%' OR t7.field1 LIKE '%TEXT5%' OR t7.field1 LIKE '%TEXT6%' OR (t7.field1 LIKE '%TEXT7%' AND t7.field1 NOT LIKE '%TEXT8%') COLLATE NOCASE)");

#Wonton Here is a suggestion use MVP design this is where you have a Database Model where you have a bunch of get and set for each item in the database table.
Next handle all your CRUD in an Activity called DBHelper then you can make calls to it from any other Activity with a lot less chance of errors. YES I use db.execSQL but once you have the string to update or insert in DBHelper life is good Here is an example of code from a DBHelper Activity HINT read about MVP
/* Update Record in Database*/
public void updateDBRow(String rowid,String website, String username, String password, String question,String answer, String notes){
db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(Col_WS,website);
cv.put(Col_UN,username);
cv.put(Col_PW,password);
cv.put(Col_SQ,question);
cv.put(Col_SA,answer);
cv.put(Col_NOTES,notes);
/*NOTE WHERE THE quotation MARKS ARE */
db.update(TABLE_INFO,cv, Col_ID + " = ?",new String[] { rowid });
db.close();
}

Related

Saving webview geturl and getitle in Sqlite database. Problem with syntax error apostrophe '

When im saving webview.getTitle (); in Sqlite database, if in title has apostrophe ('), then i got error -
android.database.sqlite.SQLiteException: near "at": syntax error (code 1): , while compiling: INSERT INTO Favorite VALUES('4 madhab bid'at tavassul', 'file:///android_asset/web/akaid/4maddhab/4.htm' );
My code like this
mysql objcon = new mysql(this, null, null, 1);
SQLiteDatabase db = objcon.getReadableDatabase();
db.execSQL(
"INSERT INTO Favorite VALUES('"
+ txtnombre.getText()
+ "', '"
+ txtlink2.getText()
+"' );"
);
How to solve this problem?
There is a single quote embedded within txtnombre.getText() : '4 madhab bid'at tavassul'. This causes SQLite to wrongly consider that this quote marks the end of the first value to insert.
To avoid that, you could consider manually doubling the single quotes :
db.execSQL(
"INSERT INTO Favorite VALUES('"
+ txtnombre.getText().replaceAll("'","\''")
+ "', '"
+ txtlink2.getText().replaceAll("'","\''")
+"' );"
);
I would recommend using bind parameters. With this option, your database driver handles escaping behind the hood :
q = "INSERT INTO Favorite VALUES(?, ?)";
t1 = txtnombre.getText();
t2 = txtlink2.getText();
db.rawQuery(q, new String[] { t1, t2 });
Finally, another approach in Android would be to use native method sqlEscapeString(), which is primarily built for this purpose.
Also, as commented by pskink, using insert() would better fit your use case than raw SQL.

rawQuery() in Android SQLite not compiling parameters

While trying to implement SQLite storage ran into strange behavior.
The "?"-symbols are not substituted.
My code:
public class DBHandler extends SQLiteOpenHelper {
public void writeTask(JSONObject object) throws JSONException {
SQLiteDatabase db = this.getWritableDatabase();
String id = object.get(OBJECT_ID).toString();
String content = object.toString();
String md5 = "md5"; //testing
Cursor c = db.rawQuery("INSERT OR REPLACE INTO ? ( ? , ? , ? ) VALUES ( ? , ? , ?);", new String[] {TABLE_OBJECTS, OBJECT_ID, OBJECT_CONTENT, OBJECT_MD5, id, content, md5 });
}
}
Then it throws a strange error:
android.database.sqlite.SQLiteException: near "?": syntax error (code 1): , while compiling: INSERT OR REPLACE INTO ? ( ? , ? , ? ) VALUES ( ? , ? , ?);
First mistake corrected, but still not working:
String selectQuery = "INSERT OR REPLACE INTO " + TABLE_OBJECTS + " ("
+ OBJECT_ID + "," + OBJECT_CONTENT + "," + OBJECT_MD5 + ") "
+ "VALUES ( ? , ? , ?);";
String[] args = { id, content, md5 };
Log.d("FP", selectQuery);
Cursor c = db.rawQuery(selectQuery,args);
Now database is untouched after this query. Logs show my query:
INSERT OR REPLACE INTO objects (id,content,md5) VALUES (?,?,?);
Any suggestions?
so, rawQuery() is just for SELECT.
But i still need to do escaping special characters, because content-variable is a stringified JSON and execSQL does not allow this.
You can use ? only for binding literals such as those in your VALUES(), not for identifiers such as table or column names earlier in your SQL.
If you need to use variables for identifiers, use regular string concatenation in Java.
Also note that rawQuery() alone won't execute your SQL. Consider using execSQL() instead.

how to update sqlite using expression?

I would like to do this query in SQLite:
update table1 set col1 = (? || substr (col1, col2))
where table1.id in (select id from table2 where condition);
I'm not sure how to do this. SQLiteDatabase.rawQuery doesn't work. All the other APIs I've seen don't allow the expression in the "set" part. If I could use SQLiteStatement, it would work. But that constructor is only visible in its package, not to my code :(
Ideally, I would do something like this:
String query =
"update Table1 set " +
" col1 = (? || substr (col1, col2)), " +
" col2 = ? " +
"where Table1.id in " +
" (select id from Table2 where col3 = ?)";
String[] args = new String[3];
args[0] = arg0;
args[1] = arg1;
args[2] = arg2;
SQLiteStatement statement = new SQLiteStatement (getDb(), query, args);
int rowsUpdated = 0;
try
{
rowsUpdated = statement.executeUpdateDelete();
} finally {
statement.close();
}
Any ideas? Thanks.
Usually when we want to run CRUD operations we use SQLiteDatabase.execSQL().
SQLiteDatabase.rawQuery() is generally used for select queries and it returns a Cursor with the result set.
Although rawQuery() should theoretically work because according to the docs
Runs the provided SQL and returns a Cursor over the result set.
But others have reported that it doesn't work with update queries, so I'm not entirely sure about that.

how to modify an item's value in a SQLite?

Supposing I have this sqlite database structure:
ID PRODUCT_NAME AVAILABILITY
1 foo 0
2 bar 1
3 baz 0
4 faz 1
How cand I modify the value of the AVAILABILITY fom 1 -> 0 where PRODUCT_NAME = 'bar' ?
Something like this,
Pseudocod:
db.execSQL( "UPDATE TABLE" + Table_name + "MODIFY" + availability + "=" + 0 + "WHERE" + product_name + "like ? " + 'bar');
I assume that I also have to drop and recreate table using onCreate() and onUpgrade() methods, right?
Some code will be highly appreciated.
Use this:
SQLiteDatabase db=dbHelper.getWritableDatabase();
String sql="update "+Table_name+" set availability='0' where product_name like 'bar'";
Object[] bindArgs={"bar"};
try{
db.execSQL(sql, bindArgs);
return true;
}catch(SQLException ex){
Log.d(tag,"update data failure");
return false;
}
You want update not alter. alter is for the database schema, update is for the data stored in it.
For example:
update TABLE_NAME set AVAILABILITY = 0 where PRODUCT_NAME like 'bar';
Also, do not just stick strings together to build an sql query. Use a prepared statement or other statement building library to avoid SQL injection attacks and errors.
You could also use the update(), insert(), query(), delete() methods that Android gives you
// define the new value you want
ContentValues newValues = new ContentValues();
newValues.put("AVAILABILITY", 0);
// you can .put() even more here if you want to update more than 1 row
// define the WHERE clause w/o the WHERE and replace variables by ?
// Note: there are no ' ' around ? - they are added automatically
String whereClause = "PRODUCT_NAME == ?";
// now define what those ? should be
String[] whereArgs = new String[] {
// in order the ? appear
"bar"
};
int amountOfUpdatedColumns = db.update("YourTableName", newValues, whereClause, whereArgs);
The advantage here is that you get correct SQL syntax for free. It also escapes your variables which prevents bad things to happen when you use "hax ' DROP TABLE '" as argument for ?.
The only thing that is still not safe is using column LIKE ? with arguments like "hello%world_" because % (match anything of several chars) and _ (match any 1 char) are not escaped.
You would need to escape those manually (e.g. place a ! before each _ or %) and use
String whereClause = "LIKE ? ESCAPE '!'"
String[] whereArgs = new String[] {
likeEscape("bar")
// likeEscape could be replaceAll("!", "!!").replaceAll("%", "!%").replaceAll("_", "!_") maybe
}
Btw: your single code line should work if you use
db.execSQL( "UPDATE " + Table_name + " SET " + availability + "=0 WHERE " + product_name + " like 'bar'");
SqlLite uses "SQL". You need a SQL "update"
db.execSQL( "update mytable set availability=0 where product_name like '%" + bar + "%'");
Here's a good link for SQL "select", "update", "insert" and "delete" ("CRUD") commands:
http://www.w3schools.com/sql/default.asp

Delete first N rows in android sqlite database

Please let me know how to delete n-rows in android sqlite database. I used this code:
String ALTER_TBL ="delete from " + MYDATABASE_TABLE +
"where"+KEY_ID+"in (select top 3"+ KEY_ID +"from"+ MYDATABASE_TABLE+"order by _id );";
sqLiteDatabase.execSQL(ALTER_TBL);
But it shows an error.
03-21 13:19:39.217: INFO/Database(1616): sqlite returned: error code = 1, msg = near "in": syntax error
03-21 13:19:39.226: ERROR/Database(1616): Failure 1 (near "in": syntax error) on 0x23fed8 when preparing 'delete from detail1where_id in (select top 3_idfromdetail1order by _id );'.
String ALTER_TBL ="delete from " + MYDATABASE_TABLE +
" where "+KEY_ID+" in (select "+ KEY_ID +" from "+ MYDATABASE_TABLE+" order by _id LIMIT 3);";
there is no "top 3" command in sqlite I know of, you have to add a limit
watch out for spaces when you add strings together : "delete from" + TABLE + "where" = "delete frommytablewhere"
This approach uses two steps to delete the first N rows.
Find the first N rows:
SELECT id_column FROM table_name ORDER BY id_column LIMIT 3
The result is a list of ids that represent the first N (here: 3) rows. The ORDER BY part is important since SQLite does not guarantee any order without that clause. Without ORDER BY the statement could delete 3 random rows.
Delete any row from the table that matches the list of ids:
DELETE FROM table_name WHERE id_column IN ( {Result of step 1} )
If the result from step 1 is empty nothing will happen, if there are less than N rows just these will be deleted.
It is important to note that the id_column has to be unique, otherwise more than the intended rows will be deleted. In case the column that is used for ordering is not unique the whole statement can be changed to DELETE FROM table_name WHERE unique_column IN (SELECT unique_column FROM table_name ORDER BY sort_column LIMIT 3). Hint: SQLite's ROWID is a good candidate for unique_column when deleting on tables (may not work when deleting on views - not sure here).
To delete the last N rows the sort order has to be reversed to descending (DESC):
DELETE FROM table_name WHERE unique_column IN (
SELECT unique_column FROM table_name ORDER BY sort_column DESC LIMIT 3
)
To delete the Nth to Mth row the LIMIT clause can be extended by an OFFSET. Example below would skip the first 2 rows and return / delete the next 3.
SELECT unique_column FROM table_name ORDER BY sort_column LIMIT 3 OFFSET 2
Setting the LIMIT to a negative value (e.g. LIMIT -1 OFFSET 2) would return all rows besides the first 2 resulting in deletion of everything but the first 2 rows - that could also be accomplished by turning the SELECT .. WHERE .. IN () into SELECT .. WHERE .. NOT IN ()
SQLite has an option to enable the ORDER BY x LIMIT n part directly in the DELETE statement without a sub-query. That option is not enabled on Android and can't be activated but this might be of interest to people using SQLite on other systems:
DELETE FROM table_name ORDER BY sort_column LIMIT 3
It seems that you've missed some spaces:
"where"+KEY_ID+"in..
must be:
"where "+KEY_ID+" in...
Furthermore you need to use the limit statement instead of top:
I'll do:
db.delete(MYDATABASE_TABLE, "KEY_ID > "+ value, null);
you can try this code
int id;
public void deleteRow(int id) {
myDataBase.delete(TABLE_NAME, KEY_ID + "=" + id, null);
}
String id;
public void deleteRow(String id) {
myDataBase.delete(TABLE_NAME, KEY_ID + "=\" " + id+"\"", null);
}
It is a bit long procedure but you can do it like this
first get the ids column of table from which which you want to delete certain values
public Cursor KEY_IDS() {
Cursor mCursor = db.rawQuery("SELECT KEYID " +
" FROM MYDATABASE_TABLE ;", null);
if (mCursor != null)
{
mCursor.moveToFirst();
}
return mCursor;
}
Collect it in an array list
ArrayList<String> first = new ArrayList<String>();
cursor1 = db.KEY_IDS();
cursor1.moveToFirst();
startManagingCursor(cursor1);
for (int i = 0; i < cursor1.getCount(); i++) {
reciv1 = cursor1.getString(cursor1
.getColumnIndex(DBManager.Player_Name));
second.add(reciv1);
}
and the fire delete query
for(int i = 0 ;i<second.size(); i++)
{
db.delete(MYDATABASE_TABLE KEYID +"=" + second.get(i) , null);
}
Delete first N (100) rows in sqlite database
Delete from table WHERE id IN
(SELECT id FROM table limit 100)
You can make use of the following mode: (in addition to the response provided by "zapl").
**DELETE FROM {Table-X} WHERE _ID NOT IN
(SELECT _ID FROM {Table-X} ORDER BY _ID DESC/ASC LIMIT (SELECT {Limit-Column} FROM {SpecificationTable}) );**
Where {Table-X} refers to the table you want to delete, _ID is the main unique-column
DESC/ASC - Based on whether you want to delete the top records or the last records, and finally in the "LIMIT" clause, we provide the "n" factor using another query, which calls in the {Limit-Column} from {SpecificationTable}: Which holds the value against which you want to delete them.
Hope this helps out someone.
Happy Coding.

Categories

Resources