I am working with JDBC driver and my problem is storing SQL queries in a good place. The point is that it will be making a large number of queries.
Statement s = conn.createStatement ();
s.executeUpdate ("DROP TABLE IF EXISTS animal");
s.executeUpdate (
"CREATE TABLE animal ("
+ "id INT UNSIGNED NOT NULL AUTO_INCREMENT,"
+ "PRIMARY KEY (id),"
+ "name CHAR(40), category CHAR(40))");`
Change to this...
Statement s = conn.createStatement ();
s.executeUpdate (StaticVariables.getDropTableAnimalQuery());
s.executeUpdate (StaticVariables.getCreateTableAnimalQuery());`
... and create special class with static variables
public class StaticVariables {
private static String dropTableAnimalQuery = "DROP TABLE IF EXISTS animal";
private static String createTableAnimalQuery = "CREATE TABLE animal ("
+ "id INT UNSIGNED NOT NULL AUTO_INCREMENT,"
+ "PRIMARY KEY (id),"
+ "name CHAR(40), category CHAR(40))";
public static String getDropTableAnimalQuery() {
return dropTableAnimalQuery;
}
public static String getCreateTableAnimalQuery() {
return createTableAnimalQuery;
}
}
Maybe someone has a better way to solve this
I hate that idiom of putting static constants in an interface like your StaticVariable example.
I prefer to keep constants in the class that uses them to the greatest degree possible. I'll add constants to an interface if many sub-classes that implement it need them, but there will be methods that are implemented as well.
I would recommend making your sql constants. Whether you put them in the code that will use them, or into another class created specifically to hold your constants is up to you.
public static final String CREATE_TABLE = "CREATE TABLE animal ("
+ "id INT UNSIGNED NOT NULL AUTO_INCREMENT,"
+ "PRIMARY KEY (id),"
+ "name CHAR(40), category CHAR(40))";
public static final String DROP_TABLE = "DROP TABLE IF EXISTS animal";
It is better place that queries where you need them. There is no reason to replace them into distinct class. Classes should have fields and methods, state and behavior, otherwise you kill the main idea of OOP. So to have the classes that are entirely a bunch of static strings is a bad idea.
By the way have a look at Spring Jdbc api. It really simplifies your daily work with Jdbc. It doesn't require a spring context if you don't whant to use it. But it is easier to work with jdbc using that api. Here is a small sample that may helps you too:
public int countYellowBoxes(final String color) {
final String query = "select count(*) from boxes where color = ?";
return jdbcTemplate.queryForInt(query, color);
}
Related
I'm Using OID as a primary key with auto increment, but I want to make Txn No also auto increment. Is there any way to make it auto increment? I tried to use loop, but it seems doesn't work.
When I click the "Save" one time, Next time should be Txn No "2", but I can't think of it, because I used OID to Auto increment, so Txn No can't use it.
Here is My Code:
public class DatabaseHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "Person.db";
public static final String TABLE_NAME = "Person_Table";
public static final String COL_1 = "OID";
public static final String COL_2 = "TxnNo";
public static final String COL_3 = "Name";
public static final String COL_4 = "Amount";
public static final String COL_5 = "Date";
public static final String COL_6 = "Description";
public static final String COL_7 = "Description2";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table " + TABLE_NAME + " (OID INTEGER PRIMARY KEY AUTOINCREMENT," +
"TxnNo TEXT, Name TEXT, Amount INTEGER,Date TEXT, Description TEXT,Description2 TEXT)");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
public boolean insertData(String TxnNo, String Name, String Amount, String Date, String Description, String Description2) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(COL_2, TxnNo);
contentValues.put(COL_3, Name);
contentValues.put(COL_4, Amount);
contentValues.put(COL_5, Date);
contentValues.put(COL_6, Description);
contentValues.put(COL_7, Description2);
long result = db.insert(TABLE_NAME, null, contentValues);
if (result == -1)
return false;
else
return true;
}
}
First thing with SQLite AUTOINCREMENT doesn't actually increment the ID, using INTEGER PRIMARY KEY will result in much the same, other than when you've had 9223372036854775807 rows added. All AUTOINCREMENT does is enforce an increasing number (so when the largest rowid is reached an SQLITE FULL exception occurs), otherwise (without AUTOINCREMENT) free lower numbers can be allocated, thus potentially circumventing the SQLITE FULL exception.
In fact what INTEGER PRIMARY KEY (with or without AUTOINCREMENT) does is make the columnn alias of the rowid (a normally hidden column that is given a unique 64 bit signed integer).
One of the rules is that only a single column can have INTEGER PRIMARY KEY (or PRIMARY KEY) coded per table. Which is the issue that you are facing.
A Solution
A way to do what you wish is to utilise a TRIGGER that is triggered when a new row is inserted and use it to update the inserted row with a value that is suffixed with the OID. e.g.
CREATE TRIGGER IF NOT EXISTS increment_tax_number
AFTER INSERT ON Person_Table
BEGIN
UPDATE Person_Table SET TxnNo = 'Txn no '||new.OID WHERE OID = new.OID;
END;
INSERT INTO Person_Table VALUES(null,'not a valid tax number as yet','Fred',15000,'2018-01-01','blah','more blah');
INSERT INTO Person_Table VALUES(null,'not a valid tax number as yet','Bert',25000,'2018-03-04','blah','more blah');
SELECT * FROM Person_Table;
For a new table the above results in :-
This solution could be incorporated by using :-
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table " + TABLE_NAME + " (OID INTEGER PRIMARY KEY AUTOINCREMENT," +
"TxnNo TEXT, Name TEXT, Amount INTEGER,Date TEXT, Description TEXT,Description2 TEXT)");
db.execsql("CREATE TRIGGER If NOT EXISTS increment_tax_number AFTER INSERT ON Person_Table
BEGIN
UPDATE Person_Table SET TxnNo = 'Txn no '||new.OID WHERE OID = new.OID;
END");
}
You would need to delete the App's data, uninstall the App or increase the version number (i.e. use super(context, DATABASE_NAME, null, 2)). Noting that you would lose any existing data. To not lose data would be more complicated.
A More efficient solution
Of course, there is also the option of just utilising the OID as it appears that you want the numeric part appended to Txn No, so there is no need to even have a column that is a waste. The Txn No 1, Txn No 2 etc can be generated when required.
e.g. The following will generate the Txn No purely from the OID column :-
SELECT Name, 'Txn No '||OID AS txn_no,Amount,Description,Description2 FROM Person_Table;
Resulting in :-
To incorporate this solution you don't need to do anything other than use suitable queries (although you may wish to do away with the TxnNo column)
More about AUTOINCREMENT
Using AUTOINCREMENT incurs overheads that are rarely required. The SQLite documentation includes :-
The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and
disk I/O overhead and should be avoided if not strictly needed. It is
usually not needed.
SQLite Autoincrement
The overheads are because when AUTOINCREMENT is used the algorithm used to determine the new rowid adds a second stage of getting the respective row from the sqlite_sequence table and then using the higher of this and the highest rowid in the table (without AUTOINCREMENT just the highest rowid in the table is used). So the overheads are having to maintain and access this additional table for every insert.
As such it would be more efficient to define your table with either :-
CREATE TABLE IF NOT EXISTS Person_Table (OID INTEGER PRIMARY KEY,TxnNo TEXT, Name TEXT, Amount INTEGER,Date TEXT, Description TEXT,Description2 TEXT);
If you decide to have the TxnNo column
or :-
CREATE TABLE IF NOT EXISTS Person_Table (OID INTEGER PRIMARY KEY, Name TEXT, Amount INTEGER,Date TEXT, Description TEXT,Description2 TEXT);
If using the derived Txn No (more efficient solution)
You can't have 2 AUTOINCREMENT variables in the table. But now you declere: TxnNo TEXT it's very strange.
You can look for these variants sql-auto-increment-several
It may be, Sqlite not providing autoincrement for two column.
So i have several ways
first of all you need to use "TxnNo" as INTEGER or LONG.
at the time while you insert new record get Max "TxnNo" and increase it by 1. It is possible only if your whole database is synced in local. if it's not fully synced then its may occur duplication .
Use "TxnNo" as LONG and set current time in milliseconds. this will give you unique number every time. no need to get max. I would like to prefer this way. cause i'm also using this way.
Veracode report is showing a SQL injection flaw for the below query. There are few parameters which I need to get the property file and then need to inject into my SQL, i.e. schema name, sorting order, etc.
I tried to use the %s with String.format, but veraCode is still showing it as a flaw. For the parameter it is fine, since I used map, but for schema and sorting order, it's still showing a flaw.
Any approach to solve this vulnerability?
phoneLogsQuery = "(select * from %s.SHORETEL_EVENTS_CALL_LOGS where CONVERT( date, CallDateTime,112 ) > CONVERT( date, GETDATE()-%s,112) "
+ " and (CALLER_CONTACT_ID in (:contactId) or CALLED_CONTACT_ID in (:contactId)) and EXTERNAL_CALL = 1 "
+ "UNION "
+ "select * from %s.SHORETEL_EVENTS_CALL_LOGS where CONVERT( date, CallDateTime,112 ) > CONVERT( date, GETDATE()-%s,112) "
+ " and (CALLER_CONTACT_ID in (:contactId) or CALLED_CONTACT_ID in (:contactId))"
+ " and GUILOGIN_NAME = :guiloginName and EXTERNAL_CALL = 0)"
+ " order by CallDateTime %s %s ";
phoneLogsQuery = String.format(phoneLogsQuery, schemaname, phoneLogAllData, schemaname, phoneLogAllData, sortDir, offsetQuery);
shoretelPhoneLogRow = jdbcTemplate.query(phoneLogsQuery,params,
new ShoretelPhoneLogMapper());
For column values you should use prepared statement. It makes injection impossible.Example :
jdbcTemplate.query("Select * from user where id=?"), new PreparedStatementSetter() {
public void setValues(PreparedStatement preparedStatement) throws SQLException
{
preparedStatement.setLong(1, id);
}
}, new ResultSetExtractor<User>() {
...
}});
Unfortunately, for column names, schema names, etc., you can use only concatenation or replace method:
"SELECT quantity from $tableName where event_id=?".replace("$tableName", "myTable")
The main thing that you should understand is that injection comes from end users and if those schema name, table name, etc., are inner info enduser can't change it. So you shouldn't be afraid of SQL injection.
Most of those values sadly cannot be interpolated by prepared statements, which means that you're going to have to do your own escaping. Not sure if Veracode is smart enough to detect it, but I'd go with StringEscapeUtils.escapeSql from apache commons lang
Use enum, it will resolve the issue. Enum limits the values you can pass to that parameter. Hence it will allow only given values. Even most of the security detection tools also consider this and will not report this as issue. If you do not want to expose column name, have different values in enum with constructore.
public class RequestDTO {
private SortOrder order;
private SortColumn colum;
}
public enum SortOrder {
asc,
desc
}
public enum SortColumn {
empployee_name
employee_department,
employee_salary
}
Basic premise of fixing SQL injections is to not use your request object directly but by first sanitizing, validating and creating new objects / references (if needed), then fetching values from that sanitized object.
So, create a method to sanitize, validate & get values that you wish to append for your order by clause and use that value instead of directly from request.
An append with StringBuilder will be OK provided you don't directly take values from non-sanitized request.
In my opinion, you seem to getting your %s from request directly and that is why Veracode is complaining.
I'm implementing an application that does basic queries on a mysql database,
e.g. update, insert, delete.
And I was wondering if anyone out there is as paranoid as me when it comes
to concatenating strings for queries, so instead they've written abstract methods
to do this so that it works on all tables and avoids easy typos.
This is my way of avoiding terrible errors in concatenation, but I would like to
see what others have done as well to broaden my ways.
Here's my method in pseudocode;
Say I have a mysql table named ACC that contains the following columns:
acc_no (primary key) | acc_first_name | acc_last_name
123 |John | Smith
Then I implemented a java abstract class
public abstract class Acc{
// All column values in the table Acc as constants
public static final String NUM = "acc_no";
public static final String FIRST_NAME = "acc_first_name";
public static final String LAST_NAME = "acc_last_name";
private Hashtable<String, String> hash = new Hashtable<String, String>();
public Acc(Connection con, String acc_no){
// Get values from mysql
PreparedStatement pstmt = ...SELECT * FROM ACC where ACC_NO = acc_no ....
ResultSet rs = resultset from pstmt
//Then put all the resultset rs values in hash so the hashtable have key/values
//that look something like this:
//[[NUM, "123"]
// [FIRST_NAME, "John"]
// [LAST_NAME, "Smith"]]
// The key is one of the constants in this class
// and the corresponding values are taken from mysql database
}
public String get(String column){
return hash.get(column)
}
}
So when you want to access a value in the table from a class that extends it,
it would be something like
class AccApp extends Acc{
AccApp(Connection con){
super(con, "123")
printName();
}
String printName(){
// Notice that I use static constants to get the column values
// instead of typing this.get("acc_first_name") and this.get("acc_last_name")
System.out.println(this.get(Acc.FIRST_NAME) + this.get(Acc.LAST_NAME));
}
}
Intro
I have a weird task - to write on Hibernate Criteria API (i.e. in database independent style) SQL query similar to
select * from branch b where '2/5/3/' like b.hier_path + '%'
where + is concatenation operator. Concatenation operator is database dependent '+' in MS SQL, '||' in Oracle etc.
I must use Criteria API (and no way to switch to HQL).
Problem #1 - like operator
Unfortunately, Hibernate allows to write only Criteria based on Java object property:
pCriteria.createCriteria(Branch.class).add(Restrictions.like("hierarchyPath", "2/5/3/%"));
Which is equivalent of
select * from branch where 'hier_path like 2/5/3/%'
I don't know how to swap operands of like operator.
Problem #2 - database independent concatenation
The SQL code must works on Oracle, MS SQL Server, DB2, Postgres, Sybase, MySQL, HSQLDB, Firebird (and some other new relational databases).
What I've got for now is SQL based hack:
Restrictions.sqlRestriction("? like concat({alias}.hier_path,'%')", "2/5/3/", Hibernate.STRING)
Unfortunately, concat is database dependent function that present in most from above mentioned databases (except Postgres and Firebird). The approach is a workaround and could not be used as constant solution (I'll try to add custom functions concat to databases that doesn't have it).
Conclusion
Could anybody propose an improvement to my hack (a database independent SQL) or correction to original CriteriaAPI?
UPDATE 28.09.12
concat functions appears in Postgres 9.1
You could write your own Criterion implementation, which would generate a SQL clause similar to the one you have in your question, except it would use the dialect associated with the criteria query to get the appropriate concat function and delegate the concatenation to this database-dependant concat function.
Thanks #JB Nizet. Code inspired by his ideas:
private class InverseLikeExpression extends SimpleExpression{
private static final String CONST_HQL_FUNCTION_NAME_CONCAT = "concat";
private static final String CONST_LIKE = " like ";
private static final String CONST_LIKE_SUFFIX = "'%'";
private final String propertyName;
protected InverseLikeExpression(String pPropertyName, Object pValue) {
super(pPropertyName, pValue, CONST_LIKE);
propertyName = pPropertyName;
}
#Override
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
Dialect dialect = criteriaQuery.getFactory().getDialect();
SQLFunction concatFunction = (SQLFunction) dialect.getFunctions().get(CONST_HQL_FUNCTION_NAME_CONCAT);
StringBuffer fragment = new StringBuffer();
if (columns.length>1) fragment.append('(');
for ( int i=0; i<columns.length; i++ ) {
String fieldName = concatFunction.render(Arrays.asList(new Object[] {columns[i], CONST_LIKE_SUFFIX}), criteriaQuery.getFactory());
fragment.append("?").append( getOp() ).append(fieldName);
if ( i<columns.length-1 ) fragment.append(" and ");
}
if (columns.length>1) fragment.append(')');
return fragment.toString();
}
}
My ultimate goal is to limit records that are able to be created so that I can have a trial version of my application. I'm thinking I can do this rather simply by returning an int variable within a sqlite count statement, and using a simple IF statement to determine if a new record should be created.
I call like so:
int jcount = 0;
mDbHelper.countjournals(jcount);
Right now i'm trying to execute this
public int countjournals(int jcount){
mDb.execSQL(jcount + " = SELECT COUNT(*) FROM "+DATABASE_JOURNAL_TABLE);
return jcount;
}
error that i recieve:
08-27 22:42:32.417: ERROR/AndroidRuntime(3976): android.database.sqlite.SQLiteException: near "0": syntax error: 0 = SELECT COUNT(*) FROM journals
Both the answers provided seemed to me like they should work, but i couldn't get them to. I've found a third solution that is working form me.
public long countjournals() {
return DatabaseUtils.queryNumEntries(mDb,DATABASE_JOURNAL_TABLE);
}
public int countjournals() {
SQLiteStatement dbJournalCountQuery;
dbJournalCountQuery = mDb.compileStatement("select count(*) from" + DATABASE_JOURNAL_TABLE);
return (int) dbJournalCountQuery.simpleQueryForLong();
}
Your query is incorrect, better way to do what you need is:
public int countjournals() {
Cursor dataCount = mDb.rawQuery("select count(*) from" + DATABASE_JOURNAL_TABLE, null);
dataCount.moveToFirst();
int jcount = dataCount.getInt(0);
dataCount.close();
return jcount;
}
Side note: Also note that you can't use primitive variables as references (and from you code it looks like you're trying to do so), also you can't pass in references to SQLite queries. Instead you just need to assign method (query) result to your variable.
Just insert a SPACE after from an it start working.....
Cursor dataCount = mDb.rawQuery("select count(*) from " + DATABASE_JOURNAL_TABLE, null);