简体   繁体   中英

Oracle 12c: Functions in the WITH Clause do not work with ADODB

select banner
from v$version
;


BANNER
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
"CORE   12.1.0.2.0  Production"
TNS for Solaris: Version 12.1.0.2.0 - Production
NLSRTL Version 12.1.0.2.0 - Production

With its 12c release, Oracle has added the functionality to allow the declaration of Pl/SQL functions directly at the top of an SQL statement (see https://oracle-base.com/articles/12c/with-clause-enhancements-12cr1 )

This can be quite a handy feature, esp. on projects where you need to pull data from DBs with user rights limited to SELECT statements.

The following (obviously simplified) query runs fine in Oracle SQL Developer:

with 
  function add_string(p_string in varchar2) return varchar2
  is
    --Function to add a string
    l_buffer varchar2(32767);
  begin
    l_buffer := p_string || ' works!';
    --
    return l_buffer;
    --
  end ; 
--
select add_string('Yes, it') as outVal
from dual
;

---------
OUTVAL
Yes, it works!

Now, I am trying to load the result set of a query based on the same principle into an ADODB Recordset in MS-Access 2007. I am aware that ADO does not handle queries starting with a WITH clause seamlessly (see, eg Why can't I do a "with x as (...)" with ADODB and Oracle? ). The way I usually get around this problem is to enclose the query in a SELECT block, like this:

select hey_yo 
from (
  with sub as (
    select 'hey' as hey
    from dual
  )
  select hey || ' yo!' as hey_yo
  from sub
)
;

---------
HEY_YO
hey yo!

Unfortunately, this does not seem to be legal syntax in Oracle when dealing with functions in the WITH clause:

select *
from (
  with 
    function add_string(p_string in varchar2) return varchar2
    is
      --Function to add a string
      l_buffer varchar2(32767);
    begin
      l_buffer := p_string || ' works!';
      --
      return l_buffer;
      --
    end ; 
  --
  select add_string('Yes, it') as outVal
  from dual
)
;

---------
PLS-00103: Encountered the symbol ")" when expecting one of the following:

   . , @ ; for <an identifier>
   <a double-quoted delimited-identifier> group having intersect
   minus order partition start subpartition union where connect
   sample
06550. 00000 -  "line %s, column %s:\n%s"
*Cause:    Usually a PL/SQL compilation error.

Here's the sub I'm trying to run in VBA:

Sub TestSub()

    Dim conn As ADODB.Connection
    Dim rs As ADODB.Recordset

    'Connection details
    Dim strHostName As String
    Dim nPortNum As Integer
    Dim strUser As String
    Dim strPassword As String
    Dim strServiceName As String


    Dim strConnection As String
    Dim strSQL As String

    Set conn = New ADODB.Connection

    '[... set credentials ...]

    'Open Connection
    With conn
        .ConnectionString = "Provider=MSDAORA;" & _
        "Data Source=(DESCRIPTION=(ADDRESS_LIST=" & _
        "(ADDRESS=(PROTOCOL=TCP)(HOST=" & strHostName & ")(PORT=" & nPortNum & ")))(CONNECT_DATA=(SERVICE_NAME=" & strServiceName & ")));" & _
        "User ID=" & strUser & ";Password=" & strPassword & ";"

        .Open

    End With

    Set rs = New ADODB.Recordset

    strSQL = "WITH FUNCTION add_string(p_string IN VARCHAR2) RETURN VARCHAR2 IS l_buffer VARCHAR2(32767); BEGIN l_buffer := p_string || ' works!'; RETURN l_buffer; END ; SELECT add_string('Yes, it') AS outval FROM dual"
    rs.Open strSQL, conn, adOpenStatic, adLockReadOnly

    '[... do stuff with data ...]
    rs.MoveFirst
    Debug.Print rs.Fields(0).Value

    rs.Close
    Set rs = Nothing

    conn.Close
    Set conn = Nothing

End Sub

Any ideas how to get around this problem? (Unfortunately, compiling the function in the DB is not an option for this particular project).

Update: I should mention the error I get when running the VBA code:

Run-Time error 3704: Operation is not allowed when the object is closed.

at rs.MoveFirst

Use the "Oracle Provider for OLE DB" instead of "Microsoft OLE DB Provider for Oracle". Oracle provider can be downloaded from here: 32-bit Oracle Data Access Components (ODAC) and NuGet Downloads

The Microsoft provider has been deprecated for many years, it is not further developed and should not be used anymore. Current release of Oracle provider is 12.1, ie it should support also new Oracle 12c features.

Connection string would be Provider=OraOLEDB.Oracle; ... Provider=OraOLEDB.Oracle; ... instead of Provider=MSDAORA;...

Unfortunately I can't test the following because I have only an Oracle 11 at my disposal here.

In theory it should work. But you should use a ADODB.Command to send the SQL as it is, straight away to Oracle

Try this :

Dim strConnection As String
Dim strSQL As String

Dim cmdQuery As ADODB.Command


Set conn = New ADODB.Connection

'[... set credentials ...]

'Open Connection
With conn
    .ConnectionString = "Provider=MSDAORA;" & _
    "Data Source=(DESCRIPTION=(ADDRESS_LIST=" & _
    "(ADDRESS=(PROTOCOL=TCP)(HOST=" & strHostName & ")(PORT=" & nPortNum & ")))(CONNECT_DATA=(SERVICE_NAME=" & strServiceName & ")));" & _
    "User ID=" & strUser & ";Password=" & strPassword & ";"

    .Open

End With

Set rs = New ADODB.Recordset

    strSQL = "WITH FUNCTION add_string(p_string IN VARCHAR2) RETURN VARCHAR2 IS l_buffer VARCHAR2(32767); BEGIN l_buffer := p_string || ' works!'; RETURN l_buffer; END ; SELECT add_string('Yes, it') AS outval FROM dual"

Set cmdQuery = New ADODB.Command
cmdQuery.ActiveConnection = conn
cmdQuery.CommandText = strSQL


With rs
    .LockType = adLockPessimistic
    .CursorType = adUseClient
    .CursorLocation = adUseClient
    .Open cmdQuery
End With    

'[... do stuff with data ...]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM