0

I am trying to invoke an Oracle stored procedure using the DBMS_SQL.EXECUTE function. I am passing the PL/SQL block as the input argument to the function which invokes the DB procedure using the DBMS_SQL.EXECUTE function. The procedure is having a string argument which I pass the current date in string format using the TO_DATE function. See below for the syntax I used to invoke the procedure. When the procedure is invoked the date passed is saved in the database as DATE data type. The issue I am facing is the time part of the date is truncated and the time becomes 12:00 A.M irrespective of the time that is passed to the function. Can someone please let me know what I am doing something wrong?

SQL block used to invoke the function:

plsql_block =BEGIN P_USER_TIME(to_date('21-JUL-2012 03:30:30','DD-MON-YYYY HH24:MI:SS')); Return; End;

SQL syntax of the function invoking the procedure :

ret_int := DBMS_SQL.OPEN_CURSOR;

DBMS_SQL.PARSE(ret_int,plsql_block,DBMS_SQL.NATIVE);

ret_int_execute := DBMS_SQL.EXECUTE(ln_dbms_cur);

PFB the oracle code block

I am using a function that takes date as varchar input and the n passes it to another procedure.I could see that the time past associated with the date is not getting inserted at some scenario's. please find below my function

iv_plsql4 :='10-08-2012 07:30:30';
ln_dbms_cur := DBMS_SQL.OPEN_CURSOR;
iv_plsql2 := BEGIN PKG_PRADEEP.P_INSERTDATE(to_date(iv_plsql4,'DD-MM-YYYY HH24:MI:SS'));

DBMS_OUTPUT.put_line(iv_plsql);
DBMS_SQL.PARSE(ln_dbms_cur,iv_plsql,DBMS_SQL.NATIVE);
ln_cur_execute := DBMS_SQL.EXECUTE(ln_dbms_cur);
This code inserts the date in to the database but the time comes as 12:00 A.M.

but if I change the string iv_plsql2 as given below the date gets inserted with the TIME field.

iv_plsql2 := BEGIN PKG_PRADEEP.P_INSERTDATE(to_date('10-AUG-2012 07:30:30','DD-MM-YYYY HH24:MI:SS'));

Can someone explain why this happens?

Cooldude
  • 31
  • 3
  • 9
  • Your PL/SQL block doesn't seem to do anything but call `P_USER_TIME`. Does this procedure truncate the date? There's no need to `return` btw. – Ben Jul 30 '12 at 21:56
  • I think we really need to see how the function and procedure are declared, and how they're being called; how are you building the `plsql_block` string? I suspect the `to_date()` is happening earlier than you think, and that the `date` is being implicitly converted back to a `varchar2` using your NLS_DATE_FORMAT (e.g. `DD-MON-YYYY`) - because the procedure has a string argument, not a date - and it's that which is losing the time portion. – Alex Poole Jul 30 '12 at 22:18
  • Ben- I am not sure whether the procedure truncates the date. – Cooldude Jul 30 '12 at 23:13

2 Answers2

0

My first stance on this would be that the STP P_USER_TIME() is truncating the timestamp. However, if you are sure that it possibly is NOT doing so, you may try-

DECLARE
   ret_int INTEGER;
   plsql_block  VARCHAR2(1000);
BEGIN
   plsql_block :='BEGIN P_USER_TIME(to_timestamp(''21-JUL-2012 03:30:30'',''DD-MON-YYYY HH24:MI:SS'')); END;';
   ret_int := DBMS_SQL.OPEN_CURSOR;
   DBMS_SQL.PARSE(ret_int,plsql_block,DBMS_SQL.NATIVE);
   ret_int_execute := DBMS_SQL.EXECUTE(ret_int);
   DBMS_SQL.CLOSE_CURSOR(ret_int);
EXCEPTION
WHEN OTHERS THEN
   DBMS_SQL.CLOSE_CURSOR(ret_int);
END;

Note- there are a number of Date-time related bugs related to ODBC drivers. For example - Bug 11864041 - TIMESTAMP VARIABLE PASSED AS VARCHAR FROM ODBC CAUSING VALUE TO BE CORRUPT (Oracle Support)

Anjan Biswas
  • 7,746
  • 5
  • 47
  • 77
  • Hi Annjawn,Thanks for your suggestion.In my case currently the function is working fine when I call the procedure from the SQL developer but when I try calling the function from my java application again the date gets stored with time as 12:00 A.M. . Are you aware of any such issues? – Cooldude Aug 06 '12 at 05:27
  • Yes, JDBC driver has a number of issues with date-timestamp values especially because date is handled entirely differently in Java. You may overcome this issue at some point but there are no guarantees that it will continue to work. The best/safe bet here is to have the date column as a VARCHAR(14) value YYYYMMDDHHMISS and then do TO_DATE while selecting from the column as needed. We are facing such date related issue for a long time related to Java and Oracle and there are a number of bugs logged by Oracle support but with no real solution. – Anjan Biswas Aug 06 '12 at 14:04
  • @Annjawn - storing dates in a `varchar2` field is a horrible solution. It confuses the optimiser, makes all interaction more complicated than it needs to be, will also break one day, and stops you using an index on the column. See [this](http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:77598210939534#913884100346401677). – Alex Poole Aug 06 '12 at 16:09
  • If you really have a driver problem that can't be fixed properly, a slightly better solution is to make any date you pass a string; so if you had `some_date > ?` and had a problem making the parm from a Java Date, could instead do `some_date > to_date(?,?)`, make the first parm a String from a SimpleDateFormatter, and make the second parm the same format mask (or the DB equivalent). In this case the OP is passing a string anyway to the function, so it's probably just converting the Java Date to a String with an unexpected format, or something. – Alex Poole Aug 06 '12 at 16:13
  • @AlexPoole I know that is a horrible solution. But the solution you offered would probably not work either since the problem persists at the oc4j/driver level, any date formater or any date format in java does not work properly in terms of Oracle Date datatypes. we have been having issues with this since the past three years in our applications and none (Oracle/java) have been able to fully resolve this for us. So my point was its better to have correct data than to have incorrect one even if it sacrifices technical knick-knacks (like- indexing etc.). – Anjan Biswas Aug 06 '12 at 18:11
  • And as far as confusion goes, do you really think that users would be running queries against DB tables in SQLPlus, toad, et.al.? Answer would be - NO, they would like to interact with the DB through an Application, and any application should be capable enough to format the `VARCHAR` to the desired date-time format. For time sensitive applications and reporting, the time component of `DATE` becomes pretty important, so having "maybe" solutions to that would be just like crossing fingers and hoping the bomb wouldn't explode. – Anjan Biswas Aug 06 '12 at 18:13
  • @Annjawn - if you have `some_date > to_date(?, 'YYYYMMDDHH24MISS')`, say, and you set the parm with `setString`, from a `SimpleDateFormatter` with format `yyyyMMddHHmmss`, the driver never sees a date. So what's the problem here? I've used this kind of thing without any issues. – Alex Poole Aug 06 '12 at 18:26
  • Depends on the application, and the stage. In dev I'd expect direct queries, and even in production from support people maybe. Then there are reporting tools, multiple applications sharing the data... You may be able to control who (or what) can insert/update, at least for now. Indexes are kind of important. You're potentially sacrificing performance, as well as maintainability and integrity, for a problem I'm not sure exists. – Alex Poole Aug 06 '12 at 18:31
  • Well then I think we should agree to disagree. You not facing the problem doesn't necessarily mean others wont, your environment/hardware etc. may be entirely different. Also, If I am a developer (in Development phase) and if I get confused looking at a column named `DATE_TIME` and data like `200120304123455`, then I think I should quit programming. As far as actual users, they are "actual business users" not support or tech support, and I don't expect or suggest direct queries to them at any cost. And for starters you can have a look at Bug 12983483 in Oracle support if you want to. – Anjan Biswas Aug 06 '12 at 20:16
  • Up to a point - I obviously can't stop you doing this, even if you never query on date columns, but I'm not going ignore what I consider bad advice being given to others. I'll point out two things though: (1) I didn't say developers would be confused, I said the *optimiser* would be; though I'm curious how you make sure no-one enters an invalid date like 20130229. (Or the one in your last comment). And (2) I'm passing a *String* as a parameter to avoid problems with Date/Timestamp; that bug (and others) are when native types are used. I'm *not* suggesting there are no bugs. – Alex Poole Aug 06 '12 at 21:12
  • 1. User doesn't enter 20130229. (S)he enters a date in a date field in an Application field. i don't expect them to run `INSERTS`. And then before the actual `INSERT` to a `TO_CHAR`. 2. Well pass a string but you have to format it using `some` method in java before passing it to Oracle's `to_date` or else you may be passing '20120412ABCDE`. You feel my solution was a bad advice for others, well I feel yours was (or could be) a bad advice too. And I note again- We should agree to disagree. – Anjan Biswas Aug 06 '12 at 21:25
0

There seems to be a contradiction in what you've said, but the flow isn't very clear. You have a procedure (P_USER_TIME?) which is called from a function (unnamed). Your procedure (and it would be helpful to at least see how that's declared) takes a string argument, but you are passing it a date, not a varchar2 value. Since we don't have your actual procedure, let's make one up:

create or replace procedure p_user_time(p_time varchar2) is
    l_time date;
begin
    dbms_output.put_line('Parameter p_time: ' || p_time);
    l_time := to_date(p_time, 'DD-MON-YYYY HH24:MI:SS');
    dbms_output.put_line('Converted l_time: ' ||
        to_char(l_time, 'DD-MON-YYYY HH24:MI:SS'));
end;
/

If I call that with the string you're passing as plsql_block I get:

alter session set nls_date_format = 'DD-MON-YYYY';
set serveroutput on

exec P_USER_TIME(to_date('21-JUL-2012 03:30:30','DD-MON-YYYY HH24:MI:SS'));

Session altered.

Parameter p_time: 21-JUL-2012
Converted l_time: 21-JUL-2012 00:00:00

PL/SQL procedure successfully completed.

So the time portion is lost. You're converting a string representing the date into a date object, and when it's passed to the procedure it's being implicitly converted back to a string using your default NLS_DATE_FORMAT mask, which I've guessed is probably DD-MON-YYYY; so this is equivalent to doing:

exec P_USER_TIME(to_char(to_date('21-JUL-2012 03:30:30','DD-MON-YYYY HH24:MI:SS')));

Doing to_char(to_date(...)) looks redundant, but because you have an explicit data mask one way and an implicit one the other, it's probably not doing what you expect.

Assuming the P_USER_TIME procedure is expecting the date/time string in the specific format you're passing, you should just pass the string, not try to convert it yourself:

exec P_USER_TIME('21-JUL-2012 03:30:30');

Parameter p_time: 21-JUL-2012 03:30:30
Converted l_time: 21-JUL-2012 03:30:30

PL/SQL procedure successfully completed.

You also have a function that's calling the procedure dynamically. Again, let's make one up:

create or replace function f_foo return number is
    ln_dbms_cur number;
    ret_int number;
    plsql_block varchar2(256);
begin
     plsql_block := 'BEGIN P_USER_TIME(to_date(''21-JUL-2012 03:30:30'',''DD-MON-YYYY HH24:MI:SS'')); END;';

     ln_dbms_cur := DBMS_SQL.OPEN_CURSOR;
     DBMS_SQL.PARSE(ln_dbms_cur, plsql_block, DBMS_SQL.NATIVE);
     ret_int := DBMS_SQL.EXECUTE(ln_dbms_cur);
     DBMS_SQL.CLOSE_CURSOR(ln_dbms_cur);
     return ret_int;
end;
/

var rc number;

exec :rc := f_foo;

Parameter p_time: 21-JUL-2012
Converted l_time: 21-JUL-2012 00:00:00

PL/SQL procedure successfully completed.

So the same thing happens. If the construction of plsql_block is simplified to:

    plsql_block := 'BEGIN P_USER_TIME(''21-JUL-2012 03:30:30''); END;';

then you get:

Parameter p_time: 21-JUL-2012 03:30:30
Converted l_time: 21-JUL-2012 03:30:30

PL/SQL procedure successfully completed.

Reading the question again, it may actually be a much simpler underlying problem. You said '... string argument to which I pass the current date in string format using the to_date function'. If that's interpreted literally, it suggests you might just be using to_date when you should have to_char; if you really want the current time that would make the line in the function something like:

 plsql_block := 'BEGIN P_USER_TIME(to_char(sysdate, ''DD-MON-YYYY HH24:MI:SS'')); END;';

Or using the direct call to the procedure:

exec P_USER_TIME(to_char(sysdate, 'DD-MON-YYYY HH24:MI:SS'));

Parameter p_time: 31-JUL-2012 09:38:43
Converted l_time: 31-JUL-2012 09:38:43

PL/SQL procedure successfully completed.

Edited to look at the Java code posted as a comment

Your function now seems to take two arguments, one of which is the block you want to execute; and return a cursor. I'm going to guess (again) that the cursor is returning what's been inserted, so I've changed my dummy procedure to insert the date/time into a table, and my function to retrieve that. This would be a lot easier if you posted a complete set of code that demonstrates the problems you're seeing, of course.

create or replace procedure p_user_time(p_time varchar2) is
    l_time date;
begin
    dbms_output.put_line('Parameter p_time: ' || p_time);
    l_time := to_date(p_time, 'DD-MON-YYYY HH24:MI:SS');
    dbms_output.put_line('Converted l_time: ' ||
        to_char(l_time, 'DD-MON-YYYY HH24:MI:SS'));
    insert into cooldude values(l_time);
end;
/

create or replace function f_foo(pNumber number, p_plsql_block in varchar2)
return sys_refcursor is
    ln_dbms_cur number;
    ret_int number;
    plsql_block varchar2(256);
    rc sys_refcursor;
begin    
     ln_dbms_cur := DBMS_SQL.OPEN_CURSOR;
     DBMS_SQL.PARSE(ln_dbms_cur, p_plsql_block, DBMS_SQL.NATIVE);
     ret_int := DBMS_SQL.EXECUTE(ln_dbms_cur);
     DBMS_SQL.CLOSE_CURSOR(ln_dbms_cur);

     open rc for select * from cooldude;
     return rc;
end;
/

I can still call that from SQL*Plus with no problems. And I can have a Java program execute it:

import java.sql.*;
import java.text.*;
import oracle.jdbc.*;
import oracle.jdbc.pool.OracleDataSource;

public class Cooldude
{

    public static void main(String args[]) throws SQLException 
    {
        String plSqlBlk = "BEGIN P_USER_TIME(to_char(sysdate, 'DD-MON-YYYY HH24:MI:SS')); END;";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Connection conn;
        OracleDataSource ds = new OracleDataSource();
        ds.setURL("jdbc:oracle:thin:scott/oracle@127.0.0.1:1521:orcl");
        conn = ds.getConnection();

        CallableStatement cstmt = null;
        ResultSet rs = null;
        String output = "";
        System.out.println("******calling SP *******");
        cstmt = conn.prepareCall("{? = call f_foo(?,?)}");
        cstmt.setFetchSize(10000);
        cstmt.registerOutParameter(1, OracleTypes.CURSOR);
        cstmt.setInt(2, 204149885);
        cstmt.setString(3, plSqlBlk);
        cstmt.executeQuery();

        rs = (ResultSet) cstmt.getObject(1);
        while (rs.next())
        {
            Timestamp ts = rs.getTimestamp(1);
            System.out.println(sdf.format(ts));
        }

        if ( conn != null )
        {
            try { conn.close(); } catch ( Exception ex ) {}
            conn = null;
        }
    }
}

javac Cooldude.java && java Cooldude

******calling SP *******
2012-08-11 09:45:07
2012-08-11 09:46:04
2012-08-11 09:54:33

Which seems to be fine; this has the output from three calls to the Java program.

You haven't said quite why you think the time is being truncated in your Java code. I'll go even further out on a limb... are you basing that on the display from your Java showing it as 00:00:00; and if so, are you using rs.getDate() instead of rs.getTimestamp? java.sql.Date doesn't have a time portion, unlike java.util.Date. (See this question for example).

Community
  • 1
  • 1
Alex Poole
  • 183,384
  • 11
  • 179
  • 318
  • Hi Alex,Thanks for your suggestion.In my case currently the function is working fine when I call the procedure from the SQL developer but when I try calling the function from my java application again the date gets stored with time as 12:00 A.M. . Are you aware of any such issues? – Cooldude Aug 06 '12 at 05:26
  • @Cooldude - please add your Java code to the question, olus any debugs you have of the actual call and parameters. You are probably doing an implicit conversion somewhere in the call, particularly if you aren't passing the date as a proper parameter. – Alex Poole Aug 06 '12 at 07:18
  • @Annjawn - 'The procedure is having a string argument which I pass the current date in string format using the TO_DATE function'? There may be confusion between `to_date` and `to_char`. I've said that we ned to see the fuction and procedure spec though. – Alex Poole Aug 06 '12 at 22:00
  • CallableStatement cstmt = null; ResultSet rs = null; SessionFactory sessionFactory = new Configuration().configure() .buildSessionFactory(); session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction();String output = ""; System.out .println("******calling SP *******"); cstmt = session.connection().prepareCall( "{? = call f_foo(?,?)}");cstmt.setFetchSize(10000); cstmt.registerOutParameter(1, oracle.jdbc.driver.OracleTypes.CURSOR);cstmt.setInt(2, 204149885); cstmt.setString(3, plSqlBlk); cstmt.executeQuery(); – Cooldude Aug 06 '12 at 22:52
  • PLSQK BLOCK passed is like 'BEGIN P_USER_TIME(to_char(sysdate, ''DD-MON-YYYY HH24:MI:SS'')); END;'; PLease let me know if you need further details – Cooldude Aug 06 '12 at 22:53
  • @Cooldude - please add code to the original question, not as comments, it's very hard to read. I've added a Java version that seems to work (though I don't use Hibernate), so something is still missing; I've guessed that you might just be retrieving the data incorrectly in Java but without more code it's impossible to tell. – Alex Poole Aug 11 '12 at 09:28
  • I understood your code.In my case the procedure which I am calling has a date variable and they are not doing the to_date conversion within the procedure that why I am passing the date String appended with to_date function. – Cooldude Aug 11 '12 at 09:40
  • I have added my findings on the mentioned issue also to the question.Do you any suggestions on it. – Cooldude Aug 11 '12 at 09:44
  • OK, but you said it had a string parameter. Can you explain exactly why you think the time is truncated; where do you retrieve and display it to see that? From Java? – Alex Poole Aug 11 '12 at 09:46
  • I just call the procedure via the function written by another developer to insert the rows.After the function execution I connect to the database using SQL developer and check the format of the date. – Cooldude Aug 11 '12 at 09:49
  • When I was trying to debug the issue only I tried hardcoding the date int he function and it worked but whereas if I pass the same date as argument from JAVA function the time was getting truncated.I have added that code I used for debugging also to the question – Cooldude Aug 11 '12 at 09:50