Formatting Date for Postgresql - java

I have the following
DateFormat dformat = new SimpleDateFormat("yyyy-M-d");
dformat.setLenient(false);
Date cin = dformat.parse(cinDate);
and the sql function
create or replace function search(_checkIn date, _checkOut date) returns setof Bookings as $$
declare
r Bookings;
begin
for r in
select * from Bookings
loop
if ((_checkIn between r.checkIn and r.checkOut) or (_checkOut between r.checkIn and r.checkOut)) then
return next r;
end if;
end loop;
return;
end;
$$ language plpgsql;
The date format for the postgresql is standard (default)
create table Bookings (
id serial,
status bookingStatus not null,
pricePaid money not null,
firstName text,
lastName text,
address text,
creditCard text,
checkOut date not null,
checkIn date not null,
room integer not null,
extraBed boolean not null default false,
foreign key (room) references Rooms(id),
primary key (id)
);
and I'm trying to parse a date into the function so it can return a table for me, I seem to run into the issue of date formatting (which is why I think I'm getting this error)
org.postgresql.util.PSQLException: ERROR: syntax error at or near "Feb"
So I was wondering how would I fix this problem, I don't know how to format the date properly
EDIT:
I'm calling the query like this
try {
String searchQuery = "SELECT * FROM Rooms r where r.id not in (select * from search(" + cin +", " + cout +"))";
PreparedStatement ps = conn.prepareStatement(searchQuery);
rs = ps.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
so I think the error comes in because the way I format the date is wrong and postgres won't read it

It sounds like you're passing the argument by concatenating them directly into the string. This is a very bad idea, since it can lead to SQL injections. Always use PreparedStatements with the ? place-holders to pass parameters, never pass them directly by concatening them directly into the query string (more so, you'd need the ' delimiters around).
You could have something like:
PreparedStatement stmt
= con.prepareStatement("SELECT id FROM Bookings WHERE checkIn=?")
stmt.setDate(1, new java.sql.Date(cin.getTime()));
// ? parameters are indexed from 1
ResultSet results = stmt.executeQuery();
Alternatively, PostgreSQL internal date conversion is usually fairly good and flexible. You could cast the string parameter to a date with PostgreSQL:
PreparedStatement stmt
= con.prepareStatement("SELECT id FROM Bookings WHERE checkIn=CAST(? AS DATE)");
stmt.setString(1, cinDate);
ResultSet results = stmt.executeQuery();
This is flexible, but might not lead to the exact result you need depending on the date format (you can check the PostgreSQL manual for details on date conversion formats). The input format you're using should work just fine, though (Try SELECT CAST('2012-05-01' AS DATE) directly in PostgreSQL, for example, this will return a correct PostgreSQL date.)
Note that when using new java.sql.Date(cin.getTime()), you're likely to run into time zone issues. You could use java.sql.Date.valueOf(...) too.
To clarify, following your edit:
This will not work, since the dates would be part of the SQL syntax itself, not strings or dates: "SELECT * FROM Rooms r where r.id not in (select * from search(" + cin +", " + cout +"))"
You'd at least need to use ' quotes: "SELECT * FROM Rooms r where r.id not in (select * from search("' + cin +"', '" + cout +"'))". Here, to a degree, you could expect the parameters to be formatted properly, but don't do it. In addition, would would still have to cast the string using CAST('...' AS DATE) or '...'::DATE.
The simplest way would certainly be:
String searchQuery = "SELECT * FROM Rooms r where r.id not in (select SOMETHING from search(CAST(? AS DATE), CAST(? AS DATE)))";
PreparedStatement ps = conn.prepareStatement(searchQuery);
ps.setString(1, cinDate);
ps.setString(2, coutDate);
(As a_horse_with_no_name pointed out in a comment, the general query wouldn't work anyway because of your inner select.)

You already have advice concerning prepared statements and proper format.
You can also largely simplify your PostgreSQL function:
CREATE OR REPLACE FUNCTION search(_checkin date, _checkout date)
RETURNS SETOF bookings AS
$BODY$
BEGIN
RETURN QUERY
SELECT *
FROM bookings
WHERE _checkin BETWEEN checkin AND checkout
OR _checkiut BETWEEN checkin AND checkout;
END;
$BODY$ language plpgsql;
Or even:
CREATE OR REPLACE FUNCTION search(_checkin date, _checkout date)
RETURNS SETOF bookings AS
$BODY$
SELECT *
FROM bookings
WHERE _checkin BETWEEN checkin AND checkout
OR _checkiut BETWEEN checkin AND checkout;
$BODY$ language sql;
Rewrite the LOOP plus conditions to a plain SQL statement which is much faster.
Return from a plpgsql function with RETURN QUERY - simpler and faster than looping.
Or use a plain sql function.
Either variant has its advantages.
No point in using mixed case identifiers without double quoting. Use all lower case instead.

According to this page, the standard format for date/time strings in SQL is:
YYYY-MM-DD HH:MM:SS
And of course for dates you can use
YYYY-MM-DD
PostgreSQL accepts other formats (see here for some details) but there's no reason not to stick to the standard.
However, since you are getting a syntax error it sounds like you are injecting the date strings into your SQL statement without the proper quoting/escaping. Double-check that you are properly escaping your input.

Related

Postgres overlapping symbol not running in java

I have a query to test two dates against two timestamp columns in the table if they overlap or not.
Query is working fine in the database client but when i added it in my java code it fails with an exception error.
I need to know how to format the && symbols in the query to be able to work.
SELECT count(*)
FROM attendance_jobs
WHERE tsrange( start_date, end_date) && tsrange(TIMESTAMP '2019-04-22', TIMESTAMP '2019-03-22 ')
Here is my java code:
long count = jdbi.withHandle(handle -> {
return handle.createQuery("select count(*) from attendance_jobs where tsrange(start_date, end_date) && tsrange(timestamp :start_date, timestamp :end_date)")
.bind("start_date", start_date)
.bind("end_date", end_date)
.mapTo(Long.class)
.findOnly();
});
The start_date and end_date data type is Timestamp.
org.jdbi.v3.core.statement.UnableToExecuteStatementException: org.postgresql.util.PSQLException: ERROR: syntax error at or near "$1"
This is just guesswork, but I think you should have a look at the usage of :start_date and :end_date again:
If start_date and end_date (java variables) are of type Timestamp you should remove the timestamp prefix to :start_date and :end_date in the query. As the documentation says, the java type Timestamp is supported by jdbi:
Out of the box, Jdbi supports the following types as SQL statement arguments:
* ...
* java.sql: Blob, Clob, Date, Time, and Timestamp
* ...
So my guess is that you have to use the query like this:
long count = jdbi.withHandle(handle -> {
return handle.createQuery("select count(*) from attendance_jobs where tsrange(start_date, end_date) && tsrange(:start_date, :end_date)")
.bind("start_date", start_date)
.bind("end_date", end_date)
.mapTo(Long.class)
.findOnly();
});
Also, but this may be personal taste, I recommend to use different spelling of bind variables and database columns. The latter with underscores (as you did), the other in camel case so it is less confusing if you use similar names. Also, it is uncommon to use underscores in java variables, so the code would look similar to this in my spelling:
Timestamp startDate = ...;
Timestamp endDate = ...;
String queryString = "select count(*) from attendance_jobs "
+ "where tsrange(start_date, end_date) && tsrange(:startDate, :endDate)";
long count = jdbi.withHandle(handle -> {
return handle.createQuery(queryString)
.bind("startDate", startDate)
.bind("endDate", endDate)
.mapTo(Long.class)
.findOnly();
});

sql-injection finding when using a prepared-statement in java [duplicate]

I am having code something like this.
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
Calculation of fullTableName is something like:
public String getFullTableName(final String table) {
if (this.schemaDB != null) {
return this.schemaDB + "." + table;
}
return table;
}
Here schemaDB is the name of the environment(which can be changed over time) and table is the table name(which will be fixed).
Value for schemaDB is coming from an XML file which makes the query vulnerable to SQL injection.
Query: I am not sure how the table name can be used as a prepared statement(like the name used in this example), which is the 100% security measure against SQL injection.
Could anyone please suggest me, what could be the possible approach to deal with this?
Note: We can be migrated to DB2 in future so the solution should compatible with both Oracle and DB2(and if possible database independent).
JDBC, sort of unfortunately, does not allow you to make the table name a bound variable inside statements. (It has its reasons for this).
So you can not write, or achieve this kind of functionnality :
connection.prepareStatement("SELECT * FROM ? where id=?", "TUSERS", 123);
And have TUSER be bound to the table name of the statement.
Therefore, your only safe way forward is to validate the user input. The safest way, though, is not to validate it and allow user-input go through the DB, because from a security point of view, you can always count on a user being smarter than your validation.
Never trust a dynamic, user generated String, concatenated inside your statement.
So what is a safe validation pattern ?
Pattern 1 : prebuild safe queries
1) Create all your valid statements once and for all, in code.
Map<String, String> statementByTableName = new HashMap<>();
statementByTableName.put("table_1", "DELETE FROM table_1 where name= ?");
statementByTableName.put("table_2", "DELETE FROM table_2 where name= ?");
If need be, this creation itself can be made dynamic, with a select * from ALL_TABLES; statement. ALL_TABLES will return all the tables your SQL user has access to, and you can also get the table name, and schema name from this.
2) Select the statement inside the map
String unsafeUserContent = ...
String safeStatement = statementByTableName.get(usafeUserContent);
conn.prepareStatement(safeStatement, name);
See how the unsafeUserContent variable never reaches the DB.
3) Make some kind of policy, or unit test, that checks that all you statementByTableName are valid against your schemas for future evolutions of it, and that no table is missing.
Pattern 2 : double check
You can 1) validate that the user input is indeed a table name, using an injection free query (I'm typing pseudo sql code here, you'd have to adapt it to make it work cause I have no Oracle instance to actually check it works) :
select * FROM
(select schema_name || '.' || table_name as fullName FROM all_tables)
WHERE fullName = ?
And bind your fullName as a prepared statement variable here. If you have a result, then it is a valid table name. Then you can use this result to build a safe query.
Pattern 3
It's sort of a mix between 1 and 2.
You create a table that is named, e.g., "TABLES_ALLOWED_FOR_DELETION", and you statically populate it with all tables that are fit for deletion.
Then you make your validation step be
conn.prepareStatement(SELECT safe_table_name FROM TABLES_ALLOWED_FOR_DELETION WHERE table_name = ?", unsafeDynamicString);
If this has a result, then you execute the safe_table_name. For extra safety, this table should not be writable by the standard application user.
I somehow feel the first pattern is better.
You can avoid attack by checking your table name using regular expression:
if (fullTableName.matches("[_a-zA-Z0-9\\.]+")) {
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
}
It's impossible to inject SQL using such a restricted set of characters.
Also, we can escape any quotes from table name, and safely add it to our query:
fullTableName = StringEscapeUtils.escapeSql(fullTableName);
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
StringEscapeUtils comes with Apache's commons-lang library.
I think that the best approach is to create a set of possible table names and check for existance in this set before creating query.
Set<String> validTables=.... // prepare this set yourself
if(validTables.contains(fullTableName))
{
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
//and so on
}else{
// ooooh you nasty haker!
}
create table MYTAB(n number);
insert into MYTAB values(10);
commit;
select * from mytab;
N
10
create table TABS2DEL(tname varchar2(32));
insert into TABS2DEL values('MYTAB');
commit;
select * from TABS2DEL;
TNAME
MYTAB
create or replace procedure deltab(v in varchar2)
is
LvSQL varchar2(32767);
LvChk number;
begin
LvChk := 0;
begin
select count(1)
into LvChk
from TABS2DEL
where tname = v;
if LvChk = 0 then
raise_application_error(-20001, 'Input table name '||v||' is not a valid table name');
end if;
exception when others
then raise;
end;
LvSQL := 'delete from '||v||' where n = 10';
execute immediate LvSQL;
commit;
end deltab;
begin
deltab('MYTAB');
end;
select * from mytab;
no rows found
begin
deltab('InvalidTableName');
end;
ORA-20001: Input table name InvalidTableName is not a valid table name ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 21
ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 16
ORA-06512: at line 2
ORA-06512: at "SYS.DBMS_SQL", line 1721

How to execute multiple depent SQLite Queries (Run SQLite Script) from Java?

I have some data in an SQLite Database. The following queries in SQLiteBrowser give me exactly the ResultSet I want:
With TempTable0 AS (SELECT Source, Date, Value AS Close FROM FinData WHERE Type = 'Close'),
TempTable1 AS (SELECT Source, Date, Value AS Colume FROM FinData WHERE Type = ' Colume')
SELECT DISTINCT TempTable0.Date, Close, Colume FROM TempTable0
JOIN TempTable1 ON TempTable1.Date = TempTable0.Date
Now, I tried to get this working as a single query via a simple executeQuery-Method from Java and read the data in a Matrixlike-DataStructure:
String sql = "With TempTable0 AS (SELECT Source, Date, Value AS Close FROM FinData WHERE Type = 'Close'), TempTable1 AS (SELECT Source, Date, Value AS Colume FROM FinData WHERE Type = ' Colume') SELECT DISTINCT TempTable0.Date, Close, Colume FROM TempTable0 JOIN TempTable1 ON TempTable1.Date = TempTable0.Date";
Connection Conn = DriverManager.getConnection(SQLCommunication.urlDB);
Statement stmt = Conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next() ) {
String res = rs.getString("Close");
DataMatrix.get("Close").add(res);
res = rs.getString("Colume");
DataMatrix.get("Colume").add(res);
}
However, the Resultset returns null.
I suspect this is because, it cannot work with two dependent SQLite-Queries, however, I have no idea, how to solve this.
I am no expert on SQLite and Java interaction and I am really running out of ideas, right now. Do you have any sugestions? (Even maybe a tip on nesting these two statements in one so it can be executed in one shot?)
Thanks so much!
Wiwi

How to write like query for date in java

Here I am going to get data based on date only but my data continence both date and time here I am using like query to select that data based on date but I am not getting it can any plz exp line it thanks.
String device = "NR09G05635";
String date = "2013-11-29";
java.util.Date temp = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).parse(date);
java.sql.Date date1 = new java.sql.Date(temp.getTime());
sql = "select * from gpsdata1 where deviceId=? and dateTime like '" + date1 + "'";
System.out.println("sql" + sql);
ps1 = con.prepareStatement(sql);
ps1.setMaxRows(1);
ps1.setString(1, device);
ps1.execute();
rs = ps1.getResultSet();
-You use the LIKE operator to compare a character, string, or CLOB value to a pattern. Case is significant. LIKE returns the BOOLEAN value TRUE if the patterns match or FALSE if they do not match
Use TO_CHAR to explicitly create a string based on a DATE, using the format you want. Don't rely on implicit conversions.
Select *
From gpsdata1
Where NVL ( TO_CHAR ( dateTime
, 'YYYY-MM-DD HH:MI:SS AM'
)
, ' ' -- One space
) Like '%';
SELECT * FROM gpsdata1
WHERE deviceId=? and CONVERT(VARCHAR(25), dateTime, 126) LIKE '2013-11-19%'
LIKE operator does not work against DATETIME variables, but you can cast the DATETIME to a VARCHAR

Error in the native query

Im new to sql and im facing a problem with the below native query
public void saveOfflineBatchDetails(BigInteger user_id) {
em.createNativeQuery("INSERT INTO rst_offline_transaction_batch (created_date , user_id)" +
"VALUES('?1', ?2)")
.setParameter(1, new java.util.Date())
.setParameter(2, user_id)
.executeUpdate();
}
It doesn't pass the values to the database. the created date should be the today's date and time. Can anyone tell me whats wrong in this query.
Thanks a lot
You generally don't include the single quotes for parameterised queries. Try:
VALUES(?1, ?2)
(without the quotes around the ?1). Otherwise it's hard (for both readers and parsers) to tell whether you wanted parameter 1 or the literal value ?1 to be inserted.
You should also check the return value from executeUpdate() to see if it thinks it affected any rows. This will probably give you zero but it's worth checking anyway.
And, finally, I think dates require special handling as per:
setParameter(1, new java.util.Date(), TemporalType.DATE);
This is because the Java Date object is not a date at all but a timestamp - you need to ensure you select the correct temporal object type so that the right value is placed in the query.
So, in short, something like:
int affected = em.createNativeQuery(
"INSERT INTO rst_offline_transaction_batch (" +
" created_date," + // ?1
" user_id" + // ?2
") VALUES (?1,?2)"
)
.setParameter(1, new java.util.Date(), TemporalType.DATE)
.setParameter(2, user_id)
.executeUpdate();
// Check affected.

Categories

Resources