|
|
![]() |
| Topic: |
Hacking' pays off (1 of 10), Read 54 times |
|
Conf: |
General PL/SQL Discussions |
|
From: |
Solomon Yakobson syakobson@erols.com |
|
Date: |
Wednesday, November 29, 2000 01:14 PM |
Today I had some free time on my hands and decided to browse through
STANDARD.SQL package on Oracle 8.1.6. I'm so glad I did. I bet most of us had
issues with DATE datatype. Not being able to store just time, not being able to
do time intervals, not being able to get date and time with less than a second
precision, not being able to handle time zones, etc, etc, etc. Let me tell you,
looks like these days are over. All the above features are already in PL/SQL -
they are just disabled. And if you dare, you can enable them (courtesy of Oracle
developers leaving comments in STANDARD.SQL). Anyway, I've done it before (I do
not know if my postings on "hacking" STANDARD.SQL are still on the
Pipeline) and I did it again. Here it is.
If you look at STANDARD.SQL you'll notice a comment:
--#### This is the end of 8.0 Standard
followed by very interesting type definitions and various functions against
them:
type TIME is new DATE_BASE; type TIMESTAMP is new DATE_BASE; type "TIME WITH TIME ZONE" is new DATE_BASE; type "TIMESTAMP WITH TIME ZONE" is new DATE_BASE; type "INTERVAL YEAR TO MONTH" is new DATE_BASE; type "INTERVAL DAY TO SECOND" is new DATE_BASE;
However, trying to declare a variable of one of these types would cause errors.
Fortunately, STANDARD.SQL has one more comment:
--#### new_names pragmas -- This is an internal pragma that restricts the use -- of particular new entries in package standard. -- It is only valid in package standard. -- Note that left out of the 8.1.5 set are non datetime -- entries urowid, "UROWID ", self_is_null and trim.
Followed by pragma new_names set for various elements. So I simply commented out
new_names pragma:
-- pragma new_names('8.1.5',
-- time,"TIME WITH TIME ZONE",
-- timestamp,"TIMESTAMP WITH TIME ZONE",
-- "INTERVAL DAY TO SECOND",
-- "INTERVAL YEAR TO MONTH",
-- to_time, to_timestamp,
-- to_time_tz, to_timestamp_tz,
-- " SYS$DSINTERVALSUBTRACT",
-- " SYS$YMINTERVALSUBTRACT",
-- to_yminterval,to_dsinterval,
-- NUMTOYMINTERVAL, NUMTODSINTERVAL,
-- current_date,
-- current_time,current_timestamp);
-- pragma new_names('8.1.6',
-- dbtimezone, sessiontimezone, localtimestamp,
-- localtime, to_local_tz, to_db_tz,
-- cube, rollup, grouping);
and recompiled package STANDARD.SQL. Guess what? It is amazing to get time up to
.000000001 sec. precision:
SQL> DECLARE t TIME; 2 BEGIN 3 t:=localtime; 4 DBMS_OUTPUT.PUT_LINE(t); 5 END; 6 / 11.07.33.000000945 AM PL/SQL procedure successfully completed.
I am not sure how Oracle managed to get nine digits after the dot. Maybe Oracle
uses internal clock? But internal clock frequency on my NT is 100MHZ which means
1 power cycle takes .00000001 of a sec and only 8 digits after the dot.
You can also get date and time with .00001 sec. precision:
SQL> DECLARE ts TIMESTAMP; 2 BEGIN 3 ts:=current_timestamp; 4 DBMS_OUTPUT.PUT_LINE(TO_CHAR(ts,'DD-Mon-YYYY HH24.MI.SS.SSSSS')); 5 END; 6 / 29-Nov-2000 11.21.38.40898 PL/SQL procedure successfully completed.
Why am I saying .00001 sec precision? Empirically I noticed fractional part is
repeated again if you specify more decimal points:
SQL> DECLARE ts TIMESTAMP; 2 BEGIN 3 ts:=current_timestamp; 4 DBMS_OUTPUT.PUT_LINE(TO_CHAR(ts,'DD-Mon-YYYY HH24.MI.SS.SSSSSSSSSSSSSSS')); 5 END; 6 / 29-Nov-2000 11.24.37.410774107741077 PL/SQL procedure successfully completed.
I am sure Oracle is testing it and fixing it, that is why all these features are
disabled in production release.
You can also get time/date and time without and with time zone:
SQL> DECLARE t TIME; 2 BEGIN 3 t:=current_time; 4 DBMS_OUTPUT.PUT_LINE(t); 5 END; 6 / 06.05.38 PM PL/SQL procedure successfully completed. SQL> DECLARE t "TIME WITH TIME ZONE"; 2 BEGIN 3 t:=current_time; 4 DBMS_OUTPUT.PUT_LINE(t); 5 END; 6 / 05.59.51 PM +01:00 PL/SQL procedure successfully completed.
Quite interesting. Database I am connected to is "hacked" 8.1.7 on Sun
Solaris on EST (GMT - 5) while client is "hacked" 8.1.6 on NT on GMT +
1 time zone. So we can assume TIME WITH TIME ZONE data type shows time converted
to client session time zone. Remember, DATE data type always returns time in
database server time zone (which, so far, we never were able to calculate):
SQL> exec dbms_output.put_line(to_char(sysdate,'HH24:MI:SS')) 12:07:30 PL/SQL procedure successfully completed.
I put "so far" since we can now. One of the functions enabled by my
"hacking" is dbtimezone:
SQL> exec dbms_output.put_line(dbtimezone) -05:00 PL/SQL procedure successfully completed.
Tells me time zone of the database I am connected to. Another function
sessiontimezone tells me my session time zone:
SQL> exec dbms_output.put_line(sessiontimezone) + 01:00 PL/SQL procedure successfully completed.
Now interval data types:
SQL> DECLARE t "TIME WITH TIME ZONE"; 2 i "INTERVAL DAY TO SECOND"; 3 BEGIN 4 t:=current_time; 5 DBMS_LOCK.SLEEP(2); 6 i:=current_time - t; 7 DBMS_OUTPUT.PUT_LINE(i); 8 END; 9 / +00 00:00:02.394452 PL/SQL procedure successfully completed.
Well, it is quite exciting but. back to reality - I have some work to do. Hope I
inspired some of you to play with these "back to the future" PL/SQL
features.
Solomon Yakobson