XQuery (DB2) replace node if it exists. Possible at all? - db2

See the following query:
select
xmlquery
(
'
if ($xml/foo/bar) then
transform copy $xml := $xml modify do replace $xml/foo/bar with <bar>some stuff</bar> return $xml
else
$xml
'
passing xmlparse(document '<foo><bar>something here</bar></foo>') as "xml"
)
from sysibm.sysdummy1
This query works just fine: It replaces the /foo/bar node and I am perfectly happy. In case that the /foo/bar node does not exist I just want nothing to happen. So the following query should just return the unmodified XML:
select
xmlquery
(
'
if ($xml/foo/bar) then
transform copy $xml := $xml modify do replace $xml/foo/bar with <bar>some stuff</bar> return $xml
else
$xml
'
passing xmlparse(document '<foo>nothing here</foo>') as "xml"
)
from sysibm.sysdummy1
Well... so I thought. In fact DB2 happily tells me:
SQL16085N The target node of an XQuery "replace" expression is not valid.
Maybe I am wrong about the syntax of the if I thought and the "then"-part is executed in both the queries above. So I tried to swap the then and the else, because clearly DB2 should not attempt to execute the then AND the else part. But that did not change anything at all.
Now I am clearly confused. Is it not possible to do what I like to do with XPath, because all path expressions must be satisfied whether they are executed or not? Or am I just doing something wrong?

I had similar problem and I solved it so:
select
xmlquery('
let $xml0 := <xml><foo><bar/></foo></xml> return
if ($xml/foo/bar) then
transform copy $xml := ($xml[foo/bar], $xml0)[1] modify
do replace $xml/foo/bar with <bar>some stuff</bar>
return $xml
else
$xml
' passing xmlparse(document '<foo>nothing here</foo>') as "xml")
from sysibm.sysdummy1
It seems like a bug

And counln't you use any type of function "exists(....)" for example fn:exists(item1, item2...) where exists function returns true if the value of the arguments IS NOT an empty sequence, otherwise it returns false?
Granted that I know nothing about db2 :)

Related

PostgreSQL absolute over relative xpath location

Consider the following xml document that is stored in a PostgreSQL field:
<E_sProcedure xmlns="http://www.minushabens.com/2008/FMSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" modelCodeScheme="Emo_ex" modelCodeSchemeVersion="01" modelCodeValue="EMO_E_PROCEDURA" modelCodeMeaning="Section" sectionID="11">
<tCatSnVsn_Pmax modelCodeScheme="Emodinamica_referto" modelCodeSchemeVersion="01" modelCodeValue="tCat4" modelCodeMeaning="My text"><![CDATA[1]]></tCatSnVsn_Pmax>
</E_sProcedure>
If I run the following query I get the correct result for Line 1, while Line 2 returns nothing:
SELECT
--Line 1
TRIM(BOTH FROM array_to_string((xpath('//child::*[#modelCodeValue="tCat4"]/text()', t.xml_element)),'')) as tCatSnVsn_Pmax_MEANING
--Line2
,TRIM(BOTH FROM array_to_string((xpath('/tCatSnVsn_Pmax/text()', t.xml_element)),'')) as tCatSnVsn_Pmax
FROM (
SELECT unnest(xpath('//x:E_sProcedure', s.XMLDATA::xml, ARRAY[ARRAY['x', 'http://www.minushabens.com/2008/FMSchema']])) AS xml_element
FROM sr_data as s)t;
What's wrong in the xpath of Line 2?
Your second xpath() doesn't return anything because of two problems. First: you need to use //tCatSnVsn_Pmax as the xml_element still starts with <E_sProcedure>. The path /tCatSnVsn_Pmax tries to select a top-level element with that name.
But even then, the second one won't return anything because of the namespace. You need to pass the same namespace definition to the xpath(), so you need something like this:
SELECT (xpath('/x:tCatSnVsn_Pmax/text()', t.xml_element, ARRAY[ARRAY['x', 'http://www.minushabens.com/2008/FMSchema']]))[1] as tCatSnVsn_Pmax
FROM (
SELECT unnest(xpath('//x:E_sProcedure', s.XMLDATA::xml, ARRAY[ARRAY['x', 'http://www.minushabens.com/2008/FMSchema']])) AS xml_element
FROM sr_data as s
)t;
With modern Postgres versions (>= 10) I prefer using xmltable() for anything nontrivial. It makes passing namespaces easier and accessing multiple attributes or elements.
SELECT xt.*
FROM sr_data
cross join
xmltable(xmlnamespaces ('http://www.minushabens.com/2008/FMSchema' as x),
'/x:E_sProcedure'
passing (xmldata::xml)
columns
sectionid text path '#sectionID',
pmax text path 'x:tCatSnVsn_Pmax',
model_code_value text path 'x:tCatSnVsn_Pmax/#modelCodeValue') as xt
For your sample XML, the above returns:
sectionid | pmax | model_code_value
----------+------+-----------------
11 | 1 | tCat4

How to search in SQL for cloumn with asterisk?

I try to create advanced search to my database.
I want to do something like that: if the user type for search = overf**w
and I have in my database an cloumn that his value = overflow - show him.
this my code:
$name = str_replace('*', '_', $name);
SELECT name FROM table WHERE name LIKE CONCAT('%', ?, '%')
its not working, I dont know what the problem.
You can't use LIKE in this situation, you need to use REGEXP() to do a wildcard search. Replace * or ** with .*. To only return names that starts with the given value use ^ at the beginning of the regular expression
SELECT name
FROM actors
WHERE name REGEXP('^overf.*w')
I don't know php but your $name parameter should be set like this (in pseudo code)
$name = '^' + replace($name, '**', '.*')

PhpOrient query returns negative Rid-s by default

I'm trying to retrieve a simple graph consisting of some Assignments that are linked to each other, however after querying one set of those assignments, the Rid-s that are returned are all negative and have nothing to do with the Rid-s in the database, so I can't run other query-s based on those Rid-s, how should I go around this, or am I doing something wrong?
Here is the code snippet responsible:
$records = $this->client->queryAsync('select rID, value, schedule, priority, type from Assignment where type = 5');
foreach ($records as $record)
{
$id = $record->getRid();
$rid = $id->__toString();
$return[$rid] = $this->client->query('TRAVERSE out("Assignment") FROM ' . $rid . ' WHILE $depth <= 5');
}
and the error that I receive:
com.orientechnologies.orient.core.exception.ORecordNotFoundException: The record with id '#-2:0' was not found
However in the database the first id is: #18:0
Hi Pirate's Lost Pearl,
is probably a problem with the transactions, orientdb makes negative RID-s when they are temporary. After the commit, the RID-s are changed to positive, here the doc
There are a couple of errors in your code:
First off you should change your __toString(); into _toString(); using a single underscore.
Then fix the $this->client->query by either switching quotation marks at the end such as " WHILE $depth <= 5" or concatenate the variable while keeping the same quotes ' WHILE ' . $depth . ' <= 5'.
OrientDB Docs | getRid()

Finding the type of query executed in perl DBI

In my perl program I have a function which takes a query as input and executes it and prints any rows fetched if the query is a select statement.
But the problem is with update or insert statements where fetchrow_array gives an error so is there any way i can know what type of query was executed so that i can skip the fetch part?
Not knowing what sort of SQL you are executing sounds like a bad idea to me, but that's not what you asked, so...
Check the docs and you will find:
You can tell if the statement was a SELECT statement by checking if
$sth->{NUM_OF_FIELDS} is greater than zero after calling execute.
Create a little helper function using a regular expressions to check the SQL string for specific words in the query...
my $sql = "UPDATE some_table SET some_value = 'abc' WHERE some_value = 'xyz'";
sub get_query_type
{
my $sql_string = shift;
my $query_type;
if( $sql_string =~ m/^UPDATE/i )
{
$query_type = "An UPDATE query";
# do UPDATE stuff
}
elsif( $sql_string =~ m/^INSERT/i )
{
$query_type = "An INSERT query";
# do INSERT stuff
}
# more blocks here as needed.
return $query_type;
}
my $type = get_query_type( $sql );
print $type;

Escaping hstore contains operators in a JDBC Prepared statement

I am using PostgreSQL 9.1.4 with hstore and the PostgreSQL JDBC driver (9.1-901.jdbc4).
I am trying to use the contains operators (?, ?&, ?|) in a PreparedStatement, however the ? character is parsed as a variable placeholder. Can this character be escaped to send the correct operator in the query?
An example:
PreparedStatement stmt = conn.prepareStatement("SELECT a, b FROM table1 WHERE c ? 'foo' AND d = ?");
stmt.setInt(1, dValue);
stmt.executeQuery();
In this form the following example would raise an exception:
org.postgresql.util.PSQLException: No value specified for parameter 2.
Update:
After investigating the query parser in the pgjdbc driver this snippet seems to indicate that it is not possible to escape the ? character. The questions that remain are:
Is there anything in the JDBC spec which allows a ? to be escaped and be anything other than a parameter placeholder?
Is there any better work around for this issue than just using plain Statements with variables manually inserted into the query string?
Effectively, it looks like the java SQL parser is not hstore compliant.
But since the syntax c ? 'foo' is equivalent to exist(c, 'foo'), you can easily workaround this problem. Have a look at the following page to see what the verbose operators for hstore are.
Postgres hstore documentation
There is a discussion about this issue on pgsql-hackers mailing list: http://grokbase.com/t/postgresql/pgsql-hackers/1325c6ys9n/alias-hstores-to-so-that-it-works-with-jdbc
For now I like most this workaround which also supports indexes:
CREATE FUNCTION exist_inline (hstore, text) RETURNS bool AS $$ SELECT $1 ? $2; $$ LANGUAGE sql;
You can use this query to find the function backing an operator in PostgreSQL like this. In your example:
SELECT
oprname,
oprcode || '(' || format_type(oprleft, NULL::integer) || ', '
|| format_type(oprright, NULL::integer) || ')' AS function
FROM pg_operator
WHERE oprname LIKE '?%'
AND (SELECT oid FROM pg_type WHERE typname = 'hstore') IN (oprleft, oprright);
This produces:
|oprname|function |
|-------|--------------------------|
|? |exist(hstore, text) |
|?| |exists_any(hstore, text[])|
|?& |exists_all(hstore, text[])|
See also a related question about using JSON operators containing ?. Note that the function usage may not profit from the same indexing capability when using a GIN index on your HSTORE column.
If you'd like to add multiple key-value pairs using PreparedStatement then you can do:
PreparedStatement ps = c.prepareStatement(
"insert into xyz(id, data) values(?, hstore(?, ?))");
ps.setLong(1, 23456L);
ps.setArray(2, c.createArrayOf("text", new String[]{"name", "city"}));
ps.setArray(3, c.createArrayOf("text", new String[]{"Duke", "Valley"}));
This will insert: 23456, 'name=>Duke, city=>Valley'