I was helping out a client a while back with an issue where a panicked email came into my inbox along the lines of “SELECT IS BROKEN IN ORACLE!!!”, which seemed perhaps a little extreme . So I pursued it further asking for some concrete details, and I must concede it had me a little bamboozled for a while. I’ve simplified the example to keep it easy to digest, but the premise is the same.
My colleague had a table with a couple of VARCHAR2 columns:
and the data inside the two columns for the single row was identical:
The panicky email was sent because a simple query to check that the two column were identical was not working as expected:
At this point, I was pretty relaxed about the situation because this is a “problem” I have seen many many times over the years, especially as people come to Oracle from other database management systems. So I responded to the email:
“Yeah…I know what the issue is. You have trailing spaces in one of the columns. Try RTRIM”
and filed the original email in that special folder where emails never resurface
But as quickly as I had hit “Send”, a reply came back with “That doesn’t work either”. Working on the assumption that my email had not been explicit enough (and perhaps I was being a little smug about it) I logged in to the system to run a query on the same table, and lo and behold – my smugness was wiped off my face
So then I tried a variation on a theme and concatenated a known value to the end of each column to see if I was missing anything:
So everything looks OK but obviously something is awry here. When you have doubts on the data, a good option is to use the DUMP function to see exactly what bytes are stored:
and the problem is revealed. The second column in the table had a trailing ASCII code zero after the word SUCCESS. This can be a nasty (and common) issue when developers are using C or similar languages to store data in an Oracle database. In many languages, ASCII zero is used to terminate an arbitrary length string, and hence it can easily end up being incorrectly stored along with the string bytes in a VARCHAR2 column especially if you are not using the delivered string handling functions in the pre-compiler layer (such as Pro*C). This can also be due to a confusion between the concept of NULL (two L’s) in the database, and the concept of the NUL (one L) character in a programming language.
Compounding the issue is that a casual glance at the data (as per the above screen shots from SQL Developer) typically suggest all is well – the NUL character is not observed. Things can get even worse – I’ve seen some GUI database tools interpret the NUL character as a true NULL and hence when a column contains only a single NUL character, those tools will report the column as being empty (ie NULL), which just makes for even more confusion. Thankfully SQL Developer does not do that, and the null indicator column can be used to see the difference between the two:
So I sent a terse email back to the developer reminding them about NUL versus NULL and that if they are using the pre-compiler correctly, then everything will take care of itself. But I must admit, I had to chuckle when they replied with a link to an AskTOM question I answered recently describing the following:
We made the same mistake ourselves! Albeit in a very specific circumstance. Look what happens when you force a STOP command onto a running scheduler job:
SQL> create table t ( x timestamp, y int ); Table created. SQL> create or replace 2 procedure myproc is 3 begin 4 for i in 1 .. 20 5 loop 6 insert into t values (systimestamp,i);commit; 7 dbms_lock.sleep(2); 8 end loop; 9 end; 10 / Procedure created. SQL> SQL> begin 2 dbms_scheduler.create_job ( 3 job_name => 'myjob', 4 job_type => 'plsql_block', 5 job_action => 'begin myproc; end;', 6 start_date => systimestamp, 7 repeat_interval => 'freq=minutely; bysecond=0;', 8 enabled => true); 9 end; 10 / PL/SQL procedure successfully completed. -- -- waited for 1 successful execution, and then stop the second one in flight -- SQL> exec dbms_scheduler.stop_job('MYJOB',force=>true) PL/SQL procedure successfully completed. SQL> select status, session_id, dump(session_id) 2 from USER_SCHEDULER_JOB_RUN_DETAILS; STATUS ------------------------------ SESSION_ID ------------------------------------------------------------- DUMP(SESSION_ID) ------------------------------------------------------------- SUCCEEDED 984,53037 Typ=1 Len=9: 57,56,52,44,53,51,48,51,55 STOPPED 983,28542 Typ=1 Len=10: 57,56,51,44,50,56,53,52,50,0 <<=== whoops!
I’ve logged this as a bug but in the interim, if you need the SESSION_ID from the scheduler views, you might want to add a: RTRIM(SESSION_ID, chr(0)) around the query.
Hoist by my own NULL petard
An alternative to DUMP in recent versions:
select json_array(‘SUCCESS’||chr(0)) make_sure from dual;
Though it wouldn’t reveal the cause like DUMP or JSON_ARRAY would show, good-old LENGTH function would give a clue showing different lengths ( excuse me for bad indentation, seem to be out of control here)
select length(col1) lcol1, length(col2) lcol2
from ( select cast(‘Succes’ as varchar2(10)) col1
, cast(‘Succes’||chr(0) as varchar2(10)) col2