views:

481

answers:

1

The docs for pipelined functions say that DML is not allowed when they are used in a SQL statement (typically a SELECT), and in most examples the pipelined functions are used for data generation or transformation (accepting a custor as parameter), but not issuing any DML statements.

Now, technically, it is possible to use SELECTs without any error from Oracle (ORA 14551 will not occur). However, I have experiences reproducible strange behavior of the select; even though PRAGMA AUTONOMOUS_TRANSACTION is not being used, the rows retrieved by the SELECT seem not always taking the current local transaction into account, which feels like a bug to me. Even more disturbing is the fact that, when using a distributed transaction (for instance via ORAMTS instead of a local transaction), the transaction is used.

Edit: As it turns out, the strange effect seems related to some WITH statements in the query, which sometimes work and sometimes not (depending on the current mood of the Oracle optimizer, at least in 10g). In some cases, I get a ORA-32036, then again it doesn't occur, without changing the code at all. Now it looks as if the queries which sometimes fail with the ORA-32036 are the ones which also fail to use the correct transaction, and it may be unrelated to the pipelined function.

So my specific questions are:

  • Is there any, preferably official, statement whether SELECTs in pipelined table functions are allowed and what their transactional context is?

  • Is there another way of modularizing commonly used queries which can be used in SQL statements (just as table functions can with TABLE())?

  • Has anyone also experienced such behavior and does maybe know more about it? I've looked into metalink, but unfortunately I didn't find anything specific on the topic.

+1  A: 

Hi Lucero,

  1. usually DML restrictions only concern modification (UPDATE, DELETE ...) statements so SELECT should be OK. I'll try to find a specific statement from Oracle.

  2. Views would be your first tool to modularize commonly-used queries.

  3. Functions have a drawback over views : if they are called from another SELECT they are not executed at the same point-in-time as the main SELECT. Each call to a SELECT is consistent but since the SELECT are in the function code and not in the main SQL you may return inconsistent results. This is not possible with views and sub-select: if a big statement call a view the view is built at the same point-in-time as the main query.

Update: regarding your comment about parameterized queries

You can build parameterized views, that is views that are dependent upon variables set before execution. Here is an example on AskTom showing how you could do it with userenv('client_info') or dbms_session.set_context.

Vincent Malgrat
Thank you for the response. Regarding 2, the problem is that I have arguments which need to be taken into account before processing the recursion. In fact, we have a historized list of graph connections and they have a creation and deletion date/time (null for deleted means not deleted). Now I have to rebuild the graph as it was on a certain date/time, so that I currently don't see how I could solve this with a view. Basically, if you know T-SQL, I'd like a way to implement something similar to the inline table functions found there, and the closest I found was the pipelined table functions.
Lucero
Regarding 3, my problem is not due to the point-in-time when they are being executed. I have tests which open a connection, start a transaction, insert data (with insert), call the SP which uses the function, and show the result. These tests fail, even though they are not running concurrently with anything else, and even in the case where this would happen, the functions are coarse enough and perform the query in a single statement (with several WITHs), so that the results should be consistent. But thanks for pointing that out.
Lucero
@Lucero: I updated my answer regarding 2. About your comment on point 3, if your function is not an autonomous transaction it should be on the same transaction and see the same data as your main query. Two questions though: 1) is your pipelined function called from the INSERT or from another SELECT? 2) Do you get the same inconsistency if you rewrite the functions NON-pipelined?
Vincent Malgrat
@Vincent, I know this technique (and I've been told that I should prefer sys_context over userenv, but the basic concept is identical). However, since I have some 6 parameters I need to pass into the "parametrized view" and as far as I can tell they return only varchars which means that they have to be casted to the type of column to be compared (in my case 2 datetime and 4 raw(16) guids), which I fear would not be very efficient. However, performance is quite critical. To answer your questions: Only in SELECT, but return it as REF CURSOR from the SP. Haven't tried non-pipelined yet, will try.
Lucero
@Vincent, regarding non-pipelined, this is a bigger change than I expected... Oracle states "Note: A table function which returns a package-level type must be pipelined." in http://www.oracle.com/technology/sample_code/tech/pl_sql/htdocs/x/Table_Functions_Cursor_Expressions/Pipelined_Table_Functions.htm
Lucero
I wasn't aware that the restriction on package-level types was lifted for pipelined functions ! I always assumed schema-level types had to be used. I'm glad I learned something =)
Vincent Malgrat
@Vincent: pipelined functions are neat in several ways, also because they allow for parallel execution (using partition) and because they are processed in batches (just like the `for x in (select)` since Oracle 9) without the need to optimize this by hand. Therefore they are memory- and computation-effective, but the problem with the transaction scope is driving me mad...
Lucero
@Vincent: I edited my question, since I may have found what is "causing" the transaction problem.
Lucero