Trying to use replace ((value),"\W","") on oracle xquery - oracle

Could someone explain why does replace \w (word-character) work and \W (non-word-character) does not .
How to solve it.
create table test (xmldata) as
select xmltype('<workbook>
<worksheet sheetName="asd-kasd" sheetId="1"/>
</workbook>')
from dual;
update test
set XMLDATA=
xmlquery(
'copy $d := .
modify (
for $i in $d/workbook/worksheet/#sheetName
return replace value of node $i with concat("1.\w:",replace(string($i/../#sheetName),"\w",""),"2. \W:",replace($i/../#sheetName,"\W",""))
)
return $d'
passing test.XMLDATA
returning content
);
ORA-19112: error raised during evaluation:
XVM-01126: [FORX0003] Regular expression matches zero-length string
fiddle:
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=ede05cbbe55f36074c36457e9491fc11
Error:
ORA-19112: error raised during evaluation:
XVM-01126: [FORX0003] Regular expression matches zero-length string
after using \W

This seems to be a bug in Oracle's implementation of the standard XQuery fn:replace() function. Using the metacharacter \W causes fn:replace to fail, on every string that I tested. I'd suggest opening a Service Request with Oracle Support to report it.
You can verify using an non-Oracle XQuery tester (e.g. here) that replace() should handle \W just fine.
Oddly, the deprecated ora:replace() function does work correctly. So you could use that as a workaround until Oracle patches the bug. But note that this function is non-standard - for example, it supports POSIX-style metacharacters (e.g. [[:alnum:]]) which the XQuery standard does not.
I simplified your query to give a more minimal verifiable example, and could reproduce the issue on Oracle 12.2.0.1. Comment out the "fn" column to get correct results.
select
regexp_replace('asd-kasd','\W','') as rr, -- normal regexp engine works fine
-- \W fails, with any string
xmlquery('fn:replace("asd-kasd","\W","")' returning content) as fn,
-- deprecated ora:replace works fine
xmlquery('ora:replace("asd-kasd","\W","")' returning content) as ora
from dual
Fiddle

Related

How to build a regular expression which makes every first letter of the word capital in SAP HANA?

How to build a regular expression which makes every first letter of the word capital ?
I tried using this :
select Upper(LEFT('this iS hana',1)) || lower(right(('this iS hana'),
length('this iS hana')-1)) FROM DUMMY;
This gives me the result :
This is hana.
whereas what I want to achieve is :
This Is Hana.
From my knowledge, you cannot achieve this using regular expressions in SAP HANA. Normally with Perl Compatible Regular Expressions (PCRE) you would be able to do something like:
SELECT REPLACE_REGEXPR('\b(\w)(\w+)'
IN 'your string here' WITH '\U\1\E\2') FROM DUMMY;
And HANA apparently supports PCRE, so it should theoretically work based on the REPLACE_REGEXPR documentation:
pattern ::= !!Perl Compatible Regular Expression
A search pattern based on Perl Compatible Regular Expression (PCRE).
Unfortunately, the \U and \E constructs seem to not be implemented in HANA.
The good news is that since HANA2 SP01, there is a new string function that can achieve roughly what you need: INITCAP. Note that this also changes the rest of the letters from inside the words to lowercase:
Converts the first character of each word in a specified string to uppercase and converts remaining characters to lowercase.
So you can just do:
SELECT INITCAP('your string here') FROM DUMMY;

String formatting in Oracle (PL/)SQL

Modern programming languages allows the developer to create strings with placeholders and replaced the correct values with a function/method usually called format. Sometimes, it looks like this:
"Hi {0}! How are you?".format('John');
Is there any function in Oracle SQL or PL/SQL with the same behavior? Or what's the best practice here?
utl_lms package, and specifically format_message() procedure of that package can be used to format a string.
begin
dbms_output.put_line(utl_lms.format_message('Hi %s! How are you %s?.'
, 'John'
, 'John'
)
);
end;
Result:
Hi John! How are you John?.
It should be noted that:
It works only within a PLS/SQL block, not SQL.
You should provide substituting value for every substituted special character (%s for string, %d for numbers) even if they are the same.

SQLAlchemy: Force column alias quoting

I want SQLAlchemy to generate the following SQL code:
SELECT t171 AS "3Harm" FROM production
I've been playing around with something similar to this SQLAlchemy ORM snippet:
session.query(Production.t171.label('3harm'))
The problem here is that this doesn't properly quote "3harm" in the generated SQL. Instead of "3harm" this generates the unquoted 3harm, which is invalid because it starts with a numerical character and therefore raises the following Oracle exception:
ORA-00923: FROM keyword not found where expected
I can get this to work by capitalizing any character in the alias name:
session.query(Production.t171.label('3Harm'))
But I would still prefer to use all lowercase column names since the rest of my program is standardized for all lowercase. Any idea how to force quote the lowercase version?
Found the solution while looking for something else.
Any column can be forced to use quotes with column.quote = True.
So for the original example:
column = Production.t171.label('3harm')
column.quote = True
session.query(column)
Success!
The SQL you want to generate isn't valid; rather than this:
SELECT t171 AS '3Harm' FROM production
... you need the identifier to be enclosed in double quotes, not single quotes:
SELECT t171 AS "3Harm" FROM production
So it looks like you should be able to do this:
session.query(Production.t171.label('"3harm"'))
or maybe:
session.query(Production.t171.label("3harm"))
But I don't use SQLAlchemy and I don't have any way to check if either is valid; you might need to escape the double quotes in the first one, for instance, though from this perhaps the second is more likely to work... and I don't understand why 3Harm would work unquoted.

ASCII for SYS_CONNECT_BY_PATH ORACLE sql

Is there any way to use ascii code for value separator in SYS_CONNECT_BY_PATH.
For example in SYS_CONNECT_BY_PATH(columnname,'!'),
I want to use the ASCII value of !(33) instead of actual symbol. Also, can i use the ascii value of ENTER (13) as value separator?
Thank you.
You can use the chr function to replace a character with it's numeric equivalent.
SYS_CONNECT_BY_PATH(column name, chr(33))
Or to use a line feed, which should also be fine:
SYS_CONNECT_BY_PATH(column name, chr(13))
It's not strictly ASCII as it depends on your character set, but it will probably work for you. You can see the numeric values using the reverse ascii function, which also isn't really quite ASCII, but again close enough especially if you're always using the same character set. So ascii('!') would give you 33.
As you've discovered, giving anything except a fixed string literal gives:
SQL Error: ORA-30003: illegal parameter in SYS_CONNECT_BY_PATH
function
30003. 00000 - "illegal parameter in SYS_CONNECT_BY_PATH function"
*Cause:
*Action: use a non-empty constant string as the second argument,
then retry the operation.
This is why I usually test things before posting, but this seemed so simple... You can get around that with replace:
REPLACE(SYS_CONNECT_BY_PATH(column name, '/'), '/', chr(33))
Borrowing an example from the manual:
SELECT LPAD(' ', 2*level-1)
||replace(SYS_CONNECT_BY_PATH(last_name, '/'),'/',chr(33)) "Path"
FROM employees
START WITH last_name = 'Kochhar'
CONNECT BY PRIOR employee_id = manager_id;
Path
--------------------------------------------------
!Kochhar
!Kochhar!Greenberg
!Kochhar!Greenberg!Faviet
!Kochhar!Greenberg!Chen
!Kochhar!Greenberg!Sciarra
!Kochhar!Greenberg!Urman
!Kochhar!Greenberg!Popp
!Kochhar!Whalen
!Kochhar!Mavris
!Kochhar!Baer
!Kochhar!Higgins
!Kochhar!Higgins!Gietz
All the credit goes to Sanjeev Chauhan. Update 7/25/2017: This turned out to be a SQL Developer 4.2.0 and 17.2.0 bug. In SQLPlus and SQL Developer 3.2.2 the statement works fine.
Fix: set secureliterals off;
The source is https://community.oracle.com/thread/4065282
I had changed the version from 4.2.0 to 4.1.1.19 and my piece of code worked. Also be aware that I couldn't find "secureliterals" in version 4.2.0

Oracle pl-sql escape character (for a " ' ")

When I am trying to execute INSERT statement in oracle, I got SQL Error: ORA-00917: missing comma error because there is a value as Alex's Tea Factory in my INSERT statement.
How could I escape ' ?
To escape it, double the quotes:
INSERT INTO TABLE_A VALUES ( 'Alex''s Tea Factory' );
In SQL, you escape a quote by another quote:
SELECT 'Alex''s Tea Factory' FROM DUAL
Instead of worrying about every single apostrophe in your statement.
You can easily use the q' Notation.
Example
SELECT q'(Alex's Tea Factory)' FROM DUAL;
Key Components in this notation are
q' which denotes the starting of the notation
( an optional symbol denoting the starting of the statement to be fully escaped.
Alex's Tea Factory (Which is the statement itself)
)' A closing parenthesis with a apostrophe denoting the end of the notation.
And such that, you can stuff how many apostrophes in the notation without worrying about each single one of them, they're all going to be handled safely.
IMPORTANT NOTE
Since you used ( you must close it with )', and remember it's optional to use any other symbol, for instance, the following code will run exactly as the previous one
SELECT q'[Alex's Tea Factory]' FROM DUAL;
you can use ESCAPE like given example below
The '_' wild card character is used to match exactly one character, while '%' is used to match zero or more occurrences of any characters. These characters can be escaped in SQL.
SELECT name FROM emp WHERE id LIKE '%/_%' ESCAPE '/';
The same works inside PL/SQL:
if( id like '%/_%' ESCAPE '/' )
This applies only to like patterns, for example in an insert there is no need to escape _ or %, they are used as plain characters anyhow. In arbitrary strings only ' needs to be escaped by ''.
SELECT q'[Alex's Tea Factory]' FROM DUAL
Your question implies that you're building the INSERT statement up by concatenating strings together. I suggest that this is a poor choice as it leaves you open to SQL injection attacks if the strings are derived from user input. A better choice is to use parameter markers and to bind the values to the markers. If you search for Oracle parameter markers you'll probably find some information for your specific implementation technology (e.g. C# and ADO, Java and JDBC, Ruby and RubyDBI, etc).
Share and enjoy.
Here is a way to easily escape & char in oracle DB
set escape '\\'
and within query write like
'ERRORS &\\\ PERFORMANCE';

Resources