NLS_NUMERIC_CHARACTERS reset when accessed from weblogic datasource - oracle

I have problem with certain code and the root cause turned out to be NLS_NUMERIC_CHARACTERS setting at session level.
when I run the query directly on database:
SQL> select 'nls_database_parameters' , p.* from nls_database_parameters p where PARAMETER = 'NLS_NUMERIC_CHARACTERS'
2 union all
3 select 'nls_session_parameters', p.* from nls_session_parameters p where PARAMETER = 'NLS_NUMERIC_CHARACTERS'
4 union all
5 select 'nls_instance_parameters', p.* from nls_instance_parameters p where PARAMETER = 'NLS_NUMERIC_CHARACTERS';
'NLS_DATABASE_PARAMETERS' PARAMETER VALUE
------------------------- ------------------------ -----
nls_database_parameters NLS_NUMERIC_CHARACTERS .,
nls_session_parameters NLS_NUMERIC_CHARACTERS .,
nls_instance_parameters NLS_NUMERIC_CHARACTERS .,
But, when I run this same query from weblogic datasource
select 'nls_database_parameters' , p.* from nls_database_parameters p where PARAMETER = 'NLS_NUMERIC_CHARACTERS'
union all
select 'nls_session_parameters', p.* from nls_session_parameters p where PARAMETER = 'NLS_NUMERIC_CHARACTERS'
union all
select 'nls_instance_parameters', p.* from nls_instance_parameters p where PARAMETER = 'NLS_NUMERIC_CHARACTERS';
-- 'NLS_DATABASE_PARAMETERS', PARAMETER, VALUE
1 nls_database_parameters NLS_NUMERIC_CHARACTERS .,
2 nls_session_parameters NLS_NUMERIC_CHARACTERS ,
3 nls_instance_parameters NLS_NUMERIC_CHARACTERS .,
The session value is wrong and is being reset by some trigger/script when accessed through weblogic datasource, I checked the weblogic startup scripts etc to see if we reset somewhere but no help.
Any pointers/ideas in this direction much appreciated if somebody faced similar issue.

I'm not familiar with the Weblogic side of things, but you can scoop up material from within Oracle by searching its source code objects (triggers, procedures, packages, functions) in DBA_SOURCE. It may contain "execute immediate" statements (or the equivalent DBMS_SQL dynamic SQL call) with something similar to:
alter session set nls_numeric_characters = ',.';
Or it could also be calling DBMS_SESSION.SET_NLS procedure:
dbms_session.set_nls('NLS_NUMERIC_CHARACTERS', '".,"');
Querying it as follows should point you to some suspects (and likely some false positives too). You can improve it by filtering by owner.
SELECT *
FROM dba_source s
WHERE (upper(s.text) LIKE '%SET\_NLS%' ESCAPE '\'
OR upper(s.text) LIKE '%NLS\_NUMERIC\_CHARACTERS%' ESCAPE '\')
AND owner != 'SYS';
The dynamic view V$SQL may have your command there if it's an anonymous PL/SQL block or script.
SELECT *
FROM v$sql s
WHERE (upper(s.sql_fulltext) LIKE '%SET\_NLS%' ESCAPE '\'
OR upper(s.sql_fulltext) LIKE '%NLS\_NUMERIC\_CHARACTERS%' ESCAPE '\')
AND s.parsing_schema_name = 'YOUR_DB_USER';
If you have already spotted the code that is causing the error and it's under control you could also fix the conversion by doing it explicitly in to_number (or to_char) with the syntax below:
SQL> select to_number('1.5', '999999D999', 'NLS_NUMERIC_CHARACTERS=,.'),
2 to_number('1.5', '999999D999', 'NLS_NUMERIC_CHARACTERS=.,')
3 from dual;
TO_NUMBER('1.5','999999D999',' TO_NUMBER('1.5','999999D999','
------------------------------ ------------------------------
15 1,5
This way you become nls session immune to it, but it may not be feasible to fix all your code to use this.

It is unlikely that this is being set explicitly. I'd wager that it is being set implicitly as a result of the user.language and user.country values used to initialize your JVM. I don't know exactly how WebLogic gets these by default but I would guess that the server's locale is set to one where the comma is the decimal separator.
You should be able to override the default WebLogic settings by setting the language and country in the startWebLogic script
set JAVA_OPTIONS=%JAVA_OPTIONS% -Duser.language=en -Duser.country=US
Of course, that may have impact other things as well (the language of error messages for example).

Related

Oracle : Update command using where clause is case sensitive?

enter image description here
SQL> select *from ORA_DEPT;
DEPARTMENT_ID DEPARTMENT_NAME
10 HR
10 Account
20 TEST
30 QA
40 Dev
SQL> update ORA_DEPT set department_name='PR' where department_name='account';
0 rows updated.
SQL> update ORA_DEPT set department_name='PR' where department_name='dev';
0 rows updated.
SQL> update ORA_DEPT set department_name='PR' where department_name='dev';
0 rows updated
SQL is generally case insensitive, in the sense that object names (table, column, procedure, bind variable etc.) are generally case insensitive. (They can be made case sensitive, but that's irrelevant here.)
You are applying the concept to data, namely to strings of characters. There is no language on Earth where strings are not case sensitive.
If you want to make the where clause case insensitive, you can do so explicitly:
...
where lower(department_name) = 'account'
Or, you can use the "nuclear option" - you can force all such comparisons to be case insensitive, by changing your NLS options. Namely:
alter session set nls_comp = linguistic;
alter session set nls_sort = binary_ci;
Note that you need both changes; if the comp parameter is binary, or if the sort parameter is not *_ci, you won't get what you want.
Use the "nuclear option" at your own risk, and only if you have a very good reason for it.
And, in any case, in your update statement, if you give the value 'PR', don't ever expect that it will be saved as 'pr', or vice versa; not unless you have a trigger that does that in the middle of the process, or some other kind of silly thing like that.

PL/SQL Developer - last character is missing in the results grid

We have a table with a column name A with type nvarchar(23).
following query will always return 23 which means that the actual length of all records are 23.
select length(trim(req.A)), count(*)
from tableName req
group by length(trim(req.A));
|length(trim(req.A))|count(*)|
------------------------------
|23 |1006 |
But when we select from this table with following query it behaves different and it seems that the last character is always removed in result Gridview in PL/SQL Developer.
select LENGTHB(req.A) lenb, length(req.A) len, req.* from tableName req
where req.A = 'NHBBBBB1398052635902235'; -- Note to the equal sign and the last charactar (5) of the where clause
the result is:
|lenb|len| A |
---------------------------------
|46 |23 |NHBBBBB139805263590223|
As you can see the last character (5) is removed in the select result.
Also when we rewrite previous query with to_char() it works fine & return correct result with the length of 23 and (5) as the last character.
select LENGTHB(req.A) lenb, length(req.A) len, to_char(req.A) from tableName req
where req.A = 'NHBBBBB1398052635902235';
the result is:
|lenb|len| to_char(req.A) |
----------------------------------
|46 |23 |NHBBBBB1398052635902235|
Can you please explain whats happen!? Is this related to PL/SQL Developer configs? How to solve this?
I believe that is a bug caused by the different character sets of oracle db you'r connected on and the client you executed that query on.
Some character sets is a mutibyte like 'AL32UTF8' character set which can use multiple bytes to store one character.
so you could check the character set of your db using:
select * from nls_database_parameters where parameter like '%SET%';
and try setting NLS_LANG character set to the character set used by your client application and try again.
hope that helps.
It seems that the problem was an old version of PL/SQL Developer which our customers used. When they updated to the newer version, the problem has been solved.

Saving Persian/Arabic Digits and Numbers inside Oracle Database

We have an Oracle Database which has many records in it. Recently we noticed that we can not save Persian/Arabic digits within a column with a datatype nvarchar2 and instead of the numbers it shows question marks "?".
I went through this to check the charset using these commands :
SELECT *
from NLS_DATABASE_PARAMETERS
WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');
and this command
SELECT USERENV('language') FROM DUAL;
The results are these two respectively:
I also issue this command :
SELECT DUMP(myColumn, 1016) FROM myTable;
And the result is like this :
Typ=1 Len=22 CharacterSet=AL16UTF16: 6,33,6,44,6,27,6,45,0,20,0,3f,0,3f,0,2f,0,3f,0,2f,0,3f
The results seem to be okay but unfortunately we still cannot save any Persian/Arabic digit within that column. however the Persian/Arabic alphabets are okay. Do you know what is the cause of this problem ?
Thank You
USERENV('language') does not return your client characters set.
So, SELECT USERENV('language') FROM DUAL; is equal to
SELECT l.value||'_'||t.value||'.'||c.value
FROM (SELECT * FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_LANGUAGE') l
CROSS JOIN (SELECT * FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_TERRITORY') t
CROSS JOIN (SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET') c;
It is not possible to get the client NLS_LANG by any SQL statement (although there seems to be a quirky workaround: How do I check the NLS_LANG of the client?)
Check your client NLS_LANG setting. It is defined either by Registry (HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG, resp. HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG) or as Environment variable. The Environment variable takes precedence.
Then you must ensure that your client application (you did not tell us which one you are using) uses the same character set as specified in NLS_LANG.
In case your application runs on Java have a look at this: Database JDBC Developer's Guide - Globalization Support
See also OdbcConnection returning Chinese Characters as "?"
Do yourself a favor and convert the main character set of your database from AR8MSWIN1256 to AL32UTF8. Most of these problems will simply go away. You can forget about NCHAR and NVARCHAR2. They are not needed anymore.
It's a one-time effort that will pay back a thounsand times.
See Character Set Migration for instructions.

Why Does Oracle 10g to_char(date time) Truncate Strings?

I got a bug report where Oracle 10g was truncating return values from to_char(datetime):
SQL> select to_char(systimestamp, '"day:"DD"hello"') from dual;
TO_CHAR(SYSTIMESTAMP,'"DAY:"DD"HE
---------------------------------
day:27hel
Notably, this does not appear to happen in Oracle 11g. My question is, why does it happen at all? Is there some configuration variable to set to tell to_char(datetime) to allocate a bigger buffer for its return value?
I'm not sure but it might be just displaying in SQL*Plus. Have you tried to run it in Toad? Or if you assign result to varchar2 in PL/SQL block and output result?
Here what I've found in SQL*Plus Reference for 10g:
The default width and format of unformatted DATE columns in SQL*Plus
is determined by the database NLS_DATE_FORMAT parameter. Otherwise,
the default format width is A9. See the FORMAT clause of the COLUMN
command for more information on formatting DATE columns.
Your values is trimmed to 9 characters which corresponds to default A9 format. I don't have same version and this behaviour is not reproducing in 11g so can you please check my theory?
I've got the same problem and I know the solution.
I use Release 11.2.0.4.0 but I beleave it is possible to repeat the situation in other versions. It somehow depends on client. (E.g. I cannot repeat it using SQL*Plus, only with PL/SQL Devepoper)
Try this:
select to_char(systimestamp, '"day:"DD"йцукенг OR any other UTF-encoded-something"') from dual
union all
select to_char(systimestamp, '"day:"DD"hello"') from dual;
You'll get the following result:
day:08йцукенг OR any other UTF-encoded-so
day:08hello
You can see the "mething" is lost. This is exactly 7 bytes exceeded because of 7 two-byte simbols "йцукенг". Oracle allocates buffer for the number of characters, not a number of required bytes.
The command
alter session set nls_length_semantics=byte/char
unfortunately does not affect this behavior.
So my solution is to cast a result as varchar2(enough_capacity)
select cast(to_char(systimestamp, '"day:"DD"йцукенг OR any other UTF-encoded-something"') as varchar(1000)) from dual
union all
select to_char(systimestamp, '"day:"DD"hello"') from dual
Explicit typecasting makes expression independent from client or configuration.
BTW, the same thing happens in all implicit to_char-conversions. E.g.
case [numeric_expression]
when 1 then '[unicode_containing_string]'
end
Result might be cutted.

NLS_NUMERIC_CHARACTERS setting for decimal

I have one db setup in a test machine and second in production machine. When I run:
select to_number('100,12') from dual
Then it gives error in test machine. However, this statement works quite fine in production machine.
Now, when I check for NLS_NUMERIC_CHARACTERS then I see ',' (comma) in both machine. Is there anywhere else I should be looking for the decimal setting?
Cheers!
You can see your current session settings by querying nls_session_parameters:
select value
from nls_session_parameters
where parameter = 'NLS_NUMERIC_CHARACTERS';
VALUE
----------------------------------------
.,
That may differ from the database defaults, which you can see in nls_database_parameters.
In this session your query errors:
select to_number('100,12') from dual;
Error report -
SQL Error: ORA-01722: invalid number
01722. 00000 - "invalid number"
I could alter my session, either directly with alter session or by ensuring my client is configured in a way that leads to the setting the string needs (it may be inherited from a operating system or Java locale, for example):
alter session set NLS_NUMERIC_CHARACTERS = ',.';
select to_number('100,12') from dual;
TO_NUMBER('100,12')
-------------------
100,12
In SQL Developer you can set your preferred value in Tool->Preferences->Database->NLS.
But I can also override that session setting as part of the query, with the optional third nlsparam parameter to to_number(); though that makes the optional second fmt parameter necessary as well, so you'd need to be able pick a suitable format:
alter session set NLS_NUMERIC_CHARACTERS = '.,';
select to_number('100,12', '99999D99', 'NLS_NUMERIC_CHARACTERS='',.''')
from dual;
TO_NUMBER('100,12','99999D99','NLS_NUMERIC_CHARACTERS='',.''')
--------------------------------------------------------------
100.12
By default the result is still displayed with my session settings, so the decimal separator is still a period.
Jaanna, the session parameters in Oracle SQL Developer are dependent on your client computer, while the NLS parameters on PL/SQL is from server.
For example the NLS_NUMERIC_CHARACTERS on client computer can be ',.' while it's '.,' on server.
So when you run script from PL/SQL and Oracle SQL Developer the decimal separator can be completely different for the same script, unless you alter session with your expected NLS_NUMERIC_CHARACTERS in the script.
One way to easily test your session parameter is to do:
select to_number(5/2) from dual;
To know SESSION decimal separator, you can use following SQL command:
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = ', ';
select SUBSTR(value,1,1) as "SEPARATOR"
,'using NLS-PARAMETER' as "Explanation"
from nls_session_parameters
where parameter = 'NLS_NUMERIC_CHARACTERS'
UNION ALL
select SUBSTR(0.5,1,1) as "SEPARATOR"
,'using NUMBER IMPLICIT CASTING' as "Explanation"
from DUAL;
The first SELECT command find NLS Parameter defined in NLS_SESSION_PARAMETERS table. The decimal separator is the first character of the returned value.
The second SELECT command convert IMPLICITELY the 0.5 rational number into a String using (by default) NLS_NUMERIC_CHARACTERS defined at session level.
The both command return same value.
I have already tested the same SQL command in PL/SQL script and this is always the same value COMMA or POINT that is displayed. Decimal Separator displayed in PL/SQL script is equal to what is displayed in SQL.
To test what I say, I have used following SQL commands:
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = ', ';
select 'DECIMAL-SEPARATOR on CLIENT: (' || TO_CHAR(.5,) || ')' from dual;
DECLARE
S VARCHAR2(10) := '?';
BEGIN
select .5 INTO S from dual;
DBMS_OUTPUT.PUT_LINE('DECIMAL-SEPARATOR in PL/SQL: (' || S || ')');
END;
/
The shorter command to know decimal separator is:
SELECT .5 FROM DUAL;
That return 0,5 if decimal separator is a COMMA and 0.5 if decimal separator is a POINT.
Best way is,
SELECT to_number(replace(:Str,',','')/100) --into num2
FROM dual;

Resources