Recently I got to know that raw query in android can not prevent SQL injection and thus I decided to convert all queries in Prepared statement which is SQL injection prevention. But I don't know how to convert complex queries in Prepared Statement.
I want to convert below queries:
1.
select
*
FROM
TableName
where
(tab1col1 in(SELECT tab2Col2 FROM MasterTable where tab2col1='Y')
or tab1col2 = CV.TRUE)
order by
tab1col3, tab1col4, tab1col5,tab1col6
2.
Select
* ,count(*) as TOTAL_COUNT ,
SUM(CASE WHEN tabCol1 LIKE '%todayDate%' THEN 1 ELSE 0 END) as TOTAL_COL1_COUNT
from
TableName
group by tabCol2;
You can use rawQuery to prevent injection by passing any arguments via the selectionargs (2nd parameter).
SQL injection, wouldn't apply to either of the queries, as they are hard coded and have no user generated/supplied inputs.
e.g. your first query could be (assuming that, 'Y' and CV.TRUE are passed as parameters (i.e. user generated/supplied) for the sake of demonstration) :-
public Cursor query1raw(String indicator1,String indicator2) {
String sql = "SELECT * " +
" FROM TableName " +
" WHERE (tab1col1" +
" IN(" +
" SELECT tab2col2 " +
" FROM MasterTable " +
" WHERE tab2col1=?)" +
" OR tab1col2=?)" +
" ORDER BY tab1col3, tab1col4,tab1col5,tab1col6";
String[] args = new String[]{indicator1,indicator2};
return mDB.rawQuery(sql,args);
}
However, the convenience methods are generally recommended rather than rawQuery or execSQL when they can be used, again using bound strings via arguments, the above, using the query convenience method could be :-
public Cursor query1(String indicator1, String indicator2) {
String whereclause = "(tab1col1 IN(SELECT tab2col2 FROM MasterTable WHERE tab2col1=?) OR tab1col2=?)";
String[] whereargs = new String[] {indicator1,indicator2};
String order_columns = "tab1col3,tab1col4,tab1col5,tab1col6";
return mDB.query("TableName",null,whereclause,whereargs,null,null,order_columns);
}
You wouldn't use prepared statements themselves as they are restricted to returning single values, not a row or rows with multiple columns.
Warning not advised
However, you could, if you really wanted, use :-
public Cursor query1ps(String indicator1,String indicator2) {
String[] whereargs = new String[] {indicator1,indicator2};
SQLiteStatement stmnt = mDB.compileStatement("SELECT * " +
" FROM TableName " +
" WHERE (tab1col1" +
" IN(" +
" SELECT tab2col2 " +
" FROM MasterTable " +
" WHERE tab2col1=?)" +
" OR tab1col2=?)" +
" ORDER BY tab1col3, tab1col4,tab1col5,tab1col6");
stmnt.bindAllArgsAsStrings(whereargs);
Log.d("PREPAREDSQL",stmnt.toString());
String sql = stmnt.toString().replace("SQLiteProgram:","");
return mDB.rawQuery(sql,null);
}
As you can see all the prepared statement is doing as such, is substituting the arguments, so has little benefit over the other methods. This would also be dependant upon SQLIteProgram: remaining constant.
The only way to prevent SQL injections is to use parameters. (In some PHP APIs, the only way to get parameters is to use prepared statements, but that is not one of the warts in the Android database API.)
Just write ? for any string, and pass the values separately:
String name = ...;
String password = ...;
cursor = db.rawQuery("SELECT SomeCol FROM Users WHERE Name = ? AND Password = ?",
new String[]{ name, password });
Please not that SQL injection could happen only if you have string values that are controlled by the (potentially-hostile) user. Your queries above do not look as if this were the case.
I am trying to delete a row from my table if 2 columns equal to what the user entered.
E.g. I have 2 textfields in which the user entered something in both e.g. "chicken" and in the other textfield "car". I want to delete the row in which those 2 values are in a row. I think it will be something like: delete from ~tablename~ where food = chicken AND vehicle = car.
Im not sure how to write that in sqlite in android.
I have my SQLitedatabase object and have called the delete method on it, but not sure what to put in the parameters
EDIT = I've managed to do it. Thanks for the below answers but this is how I've done it:
sqlitedb.delete("Random", "food =? AND vehicle=? ", new String[]{tv.getText.toString(),tv1.getText.toString()});
tv and tv1 are textfields in my case. Random is my table's name.
The sql query will look like -
String sqlQuery = "DELETE FROM <table_name> WHERE food = '"+ <food_name> + "' AND vehicle = '" + <vehicle_name> + "'";
You want something like:
String table_name=~tablename~;
String table_column_one=food;
String table_column_two=vehicle;
database.delete(table_name,
table_column_one + " = ? AND " + table_column_two + " = ?",
new String[] {"chicken", "car"});
Check SQLiteDatabase's documentation on delete function for more info.
SQLite accepts conditionals in the WHERE clause as regular SQL.
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.
I have tableA, tableB, and tableC
table A and tableB are joined by tableA.Id(PK) = tableB.tableAId(FK)
table B and tableC are joined by tableB.Id(PK) = tableC.tableBId(FK)
I want to be able to do this:
SELECT c.ALL from tableC c
INNER JOIN tableB b on c.tableBId = b.Id
INNER JOIN tableA a on b.tableAId = a.Id
WHERE a.Id = 108
I have found a lot of posts on the web which uses db.rawquery() to implement this query. However I have also heard that rawquery() is less secure than query(). So for the sake of seeking best practice as a beginner, my question is:
Is there a way to implement this query using db.query() instead of db.rawquery()?
thanks in advance.
Is there a way to implement this query using db.query() instead of
db.rawquery()?
So it's worth to say that rawQuery() makes a trick. But also exists another approach.
query() method is designed for performing queries over one table. But the best way how to JOIN tables in SQLite is to use SQLiteQueryBuilder and with setTables() method you are able to join.
Hence i recommend you to use mentioned SQLiteQueryBuilder. But it's little more complicated against rawQuery() method where you need to assign only raw statement.
If don't know how to start, check this example:
How to use a join with SQLite
Note:
Is the fact that rawQuery() is less secure than query() because query() method uses precompiled statements which are safer than "raw" statements. But always you can(should) use placeholders which significantly increase safety of statement as main protection against SQL injections and statement becomes much more human-readable as well.
This is kind of late, but I thought others who're looking for that might benefit from that:
db.query() method natively supports LEFT OUTER JOIN AND INNER JOIN via its table argument so you don't actually need to use SQLiteQueryBuilder to accomplish that. Also it's easier and and pretty much straight forward.
This method is widely used in Google I/O 2015 Schedule app's source code.
A Quick example (String constants left out for brevity):
Cursor cursor = db.query(NoteContract.Note.TABLE_NAME
+ " LEFT OUTER JOIN authors ON notes._id=authors.note_id", projection, selection,
selectionArgs, null, null, "notes._id");
The key is in the first argument to db.query().
Currently, only LEFT OUTER JOIN and INNER JOIN are supported, which is quite sufficient for most apps.
I hope this answer helps others who're looking for this.
Yes you can use query() instead of rawQuery(), given a single assumption - there are no two same column names in the tables you are joining.
If that criteria is fullfilled, then you can use this answer
https://stackoverflow.com/a/34688420/3529903
As per SharpEdge's comment and after trying a more complex example based upon Nimrod Dayan's answer, here's a more complex example.
4 joins are used, a generated column is also used. It uses an expression (subtracts timestamps) and then uses that in the WHERE clause.
Basically, the method is to append the join clauses to the table name string (SQLite then moves this for you to after the columns).
DBConstants.SQL????? is resolved to the respective SQL e.g. DBConstants.SQLISNOTNULL resolves to IS NOT NULL
DBConstans.CALCULATED????? are names for calculated columns.
DB????TableConstants.????_COL resolves to column names (.._FULL resolves to table.column e.g. to avoid ambiguous _ID columns).
The method (getToolRules) is as follows :-
public Cursor getToolRules(boolean rulesexist,
int minimumruleperiodindays,
int minimumbuycount) {
String columns[] = new String[] {
"0 " + DBConstants.SQLAS + DBConstants.STD_ID,
DBProductusageTableConstants.PRODUCTUSAGE_PRODUCTREF_COL,
DBProductusageTableConstants.PRODUCTUSAGE_AISLEREF_COL,
DBProductusageTableConstants.PRODUCTUSAGE_COST_COL,
DBProductusageTableConstants.PRODUCTUSAGE_BUYCOUNT_COL,
DBProductusageTableConstants.PRODUCTUSAGE_FIRSTBUYDATE_COL,
DBProductusageTableConstants.PRODUCTUSAGE_LATESTBUYDATE_COL,
DBProductusageTableConstants.PRODUCTUSAGE_ORDER_COL,
DBProductusageTableConstants.PRODUCTUSAGE_RULESUGGESTFLAG_COL,
DBProductusageTableConstants.PRODUCTUSAGE_CHECKLISTFLAG_COL,
DBProductusageTableConstants.PRODUCTUSAGE_CHECKLISTCOUNT_COL,
"(" +
DBProductusageTableConstants.PRODUCTUSAGE_LATESTBUYDATE_COL +
"- " +
DBProductusageTableConstants.PRODUCTUSAGE_FIRSTBUYDATE_COL +
" / (86400000)" +
") " + DBConstants.SQLAS + DBConstants.CALCULATED_RULEPERIODINDAYS,
DBProductsTableConstants.PRODUCTS_NAME_COL,
DBAislesTableConstants.AISLES_NAME_COL,
DBAislesTableConstants.AISLES_ORDER_COL,
DBAislesTableConstants.AISLES_SHOPREF_COL,
DBShopsTableConstants.SHOPS_NAME_COL,
DBShopsTableConstants.SHOPS_CITY_COL,
DBShopsTableConstants.SHOPS_ORDER_COL,
DBRulesTableConstants.RULES_ID_COL_FULL +
DBConstants.SQLAS + DBRulesTableConstants.RULES_ALTID_COL,
DBRulesTableConstants.RULES_AISLEREF_COL,
DBRulesTableConstants.RULES_PRODUCTREF_COL,
DBRulesTableConstants.RULES_NAME_COL,
DBRulesTableConstants.RULES_USES_COL,
DBRulesTableConstants.RULES_PROMPT_COL,
DBRulesTableConstants.RULES_ACTON_COL,
DBRulesTableConstants.RULES_PERIOD_COL,
DBRulesTableConstants.RULES_MULTIPLIER_COL
};
String joinclauses = DBConstants.SQLLEFTJOIN +
DBProductsTableConstants.PRODUCTS_TABLE +
DBConstants.SQLON +
DBProductusageTableConstants.PRODUCTUSAGE_PRODUCTREF_COL + " = " +
DBProductsTableConstants.PRODUCTS_ID_COL_FULL + " " +
DBConstants.SQLLEFTJOIN +
DBAislesTableConstants.AISLES_TABLE +
DBConstants.SQLON +
DBProductusageTableConstants.PRODUCTUSAGE_AISLEREF_COL + " = " +
DBAislesTableConstants.AISLES_ID_COL_FULL +
DBConstants.SQLLEFTJOIN +
DBShopsTableConstants.SHOPS_TABLE +
DBConstants.SQLON +
DBAislesTableConstants.AISLES_SHOPREF_COL + " = " +
DBShopsTableConstants.SHOPS_ID_COL_FULL +
DBConstants.SQLLEFTJOIN +
DBRulesTableConstants.RULES_TABLE +
DBConstants.SQLON +
DBProductusageTableConstants.PRODUCTUSAGE_PRODUCTREF_COL + " = " +
DBRulesTableConstants.RULES_PRODUCTREF_COL +
DBConstants.SQLAND +
DBProductusageTableConstants.PRODUCTUSAGE_AISLEREF_COL + " = " +
DBRulesTableConstants.RULES_AISLEREF_COL
;
String ruleexistoption = DBRulesTableConstants.RULES_ID_COL_FULL;
if (rulesexist) {
ruleexistoption = ruleexistoption + DBConstants.SQLISNOTNULL;
} else {
ruleexistoption = ruleexistoption + DBConstants.SQLISNULL;
}
String whereclause = DBProductusageTableConstants.PRODUCTUSAGE_BUYCOUNT_COL +
" = ?" +
DBConstants.SQLAND + ruleexistoption +
DBConstants.SQLAND +
"(" + DBConstants.CALCULATED_RULEPERIODINDAYS + " / ?) > 0" +
DBConstants.SQLAND +
DBProductusageTableConstants.PRODUCTUSAGE_BUYCOUNT_COL + " > ?";
if (minimumbuycount > 0) {
--minimumbuycount;
}
String[] whereargs = new String[] {
"0",
Integer.toString(minimumruleperiodindays),
Integer.toString(minimumbuycount)
};
return db.query(DBProductusageTableConstants.PRODUCTUSAGE_TABLE + joinclauses,
columns,whereclause,whereargs,null,null,null);
}
The base SQL, which was created in SQLite Manager, used as a guide to building the method (looks far nicer, IMHO, than the SQL extracted from the cursor in debug) is :-
Note! 0 AS _ID is used to enable the cursor to be used by a CursorAdapter (i.e. CursorAdapters require a column named _ID)
SELECT
0 AS _id,
productusage.productusageproductref,
productusage.productusageaisleref,
productusage.productusageorder,
productusage.productusagecost,
productusage.productusagebuycount,
productusage.productusagefirstbuydate,
productusage.productusagelatestbuydate,
productusage.productusagerulesuggestflag,
productusage.productusagechecklistflag,
productusage.productusagechecklistcount,
/*********************************************************************************************************************************
Calculate the period in days from between the firstbuydate and the latestbuydate
*********************************************************************************************************************************/
(productusagelatestbuydate - productusagefirstbuydate) / (1000 * 60 * 60 * 24) AS periodindays,
products.productname,
aisles.aislename,
aisles.aisleorder,
aisles.aisleshopref,
shops.shopname,
shops.shopcity,
shops.shoporder,
rules._id AS rule_id,
rules.rulename,
rules.ruleuses,
rules.ruleprompt,
rules.ruleacton,
rules.ruleperiod,
rules.rulemultiplier
FROM productusage
LEFT JOIN products ON productusageproductref = products._id
LEFT JOIN aisles ON productusageaisleref = aisles._id
LEFT JOIN shops ON aisles.aisleshopref = shops._id
LEFT JOIN rules ON productusageaisleref = rules.ruleaisleref AND productusageproductref = rules.ruleproductref
WHERE productusagebuycount > 0 AND rules._id IS NULL AND (periodindays / 2) > 0 AND productusage.productusagebuycount > 0
public HashMap<String, String> get_update_invoice_getdata(String gen) {
// TODO Auto-generated method stub
HashMap<String, String> wordList;
wordList = new HashMap<String, String>();
Cursor cur_1 = ourDataBase
.rawQuery(
"SELECT * FROM Invoice i JOIN Client c ON i.Client_id=c.Client_id JOIN TAX t ON i.Tax_id=t.Tax_id JOIN Task it ON i.Task_id=it.Task_id WHERE i.Inv_no=?",
new String[] { gen });
int intext = cur_1.getColumnIndex(C_ORG_NAME);
int intext5 = cur_1.getColumnIndex(TA_NAME);
int intext6 = cur_1.getColumnIndex(TA_RATE);
int intext7 = cur_1.getColumnIndex(TA_QTY);
int intext8 = cur_1.getColumnIndex(TA_TOTAL);
if (cur_1.moveToFirst()) {
do {
wordList.put("Org_name", cur_1.getString(intext));
wordList.put("client_id", cur_1.getString(2));
wordList.put("po_number", cur_1.getString(4));
wordList.put("date", cur_1.getString(3));
wordList.put("dis_per", cur_1.getString(7));
wordList.put("item_name", cur_1.getString(intext5));
wordList.put("item_rate", cur_1.getString(intext6));
wordList.put("item_cost", cur_1.getString(intext7));
wordList.put("item_total", cur_1.getString(intext8));
} while (cur_1.moveToNext());
}
return wordList;
}
Hi to All I am new to Android.
I am using SQLite DataBase in my Application
meanwhile I am Written Queries using +
Like delete from tablename where value = + value;
this is my query
String delete_query = "delete from " + tableName
+ " where title = '" + title + "'";
database.execSQL(delete_query);
I want to write this Query using placeholder ?.
so that i tried
database.delete(tableName, title + "?" , new String[] {title});
instead "?" i tried (?)/('?')/'?'
but it is giving me an error....
can any one tell me how to write appropriate query using ?.....
Thanks in Advance.
Mahaveer
Make sure you have put the equal sign:-
database.delete(tableName, title + "=?" , new String[] {title});
As far as possible, try to use the less raw queries you can. Two advantages:
Query parameters will be escaped by the system (protection against SQL injection)
The code will be more readable
See the delete function of SQLiteDatabase class
public int delete (String table, String whereClause, String[]
whereArgs)
Convenience method for deleting rows in the
database.
table the table to delete from
whereClause the optional WHERE clause
to apply when deleting. Passing null will delete all rows.
Returns the number of rows affected if a whereClause is passed in, 0
otherwise. To remove all rows and get a count pass "1" as the
whereClause.
In your case:
final String where = "title=?";
final String[] args = new String[] { title };
database.delete(tableName, where, args);