21

I'm not the first to have these issues, and will list some reference posts below, but am still looking for a proper solution.

I need to call a stored procedure (Oracle 10g database) from a C# web service. The web server has an Oracle 9i client installed and I am using Microsofts System.Data.OracleClient.

The procedure takes an XML as a CLOB. When the XML was over 4000 Bytes (which is likely in a normal use case), I stumbled over the following error:

ORA-01460 - unimplemented or unreasonable conversion requested

I've found this, this and this post.

Further I found a promising workaround which doesn't call the stored procedure directly from C# but defines a piece of anonymous PL/SQL code instead. This code is run as an OracleCommand. The XML is embedded as a string literal and the procedure call is done from within that piece of code:

private const string LoadXml =
    "DECLARE " +
    "  MyXML CLOB; " +
    "  iStatus INTEGER; " +
    "  sErrMessage VARCHAR2(2000); " +
    "BEGIN " +
    "  MyXML := '{0}'; " +
    "  iStatus := LoadXML(MyXML, sErrMessage); " +
    "  DBMS_OUTPUT.ENABLE(buffer_size => NULL); " +
    "  DBMS_OUTPUT.PUT_LINE(iStatus || ',' || sErrMessage); " +
    "END;";
OracleCommand oraCommand = new OracleCommand(
    string.Format(LoadXml, xml), oraConnection);
oraCommand.ExecuteNonQuery();

Unfortunately, this approach now fails as soon as the XML is over 32 KBytes or so, which still is very likely in my application. This time the error stems from the PL/SQL compiler which says:

ORA-06550: line1, column 87: PLS-00172: string literal too long

After some research I conclude that it's simply not feasible to solve the problem with my second approach.

Following the above-mentioned posts I have the following two options.

(The first post said some clients are buggy, but mine (9i) does not fall in the mentioned range of 10g/11g versions.)

Can you confirm that these are the only two options left? Or is there another way to help me out?

Just to clarify: the XML won't eventually be saved in any table, but it is processed by the stored procedure which inserts some records in some table based on the XML contents.

My considerations about the two options:

  • Switching to ODP.NET is difficult because I have to install it on a web server on which I don't have system access so far, and because we might also want to deploy the piece of code on clients, so each client would have to install ODP.NET as part of the deployment.
  • The detour over a table makes the client code quite a bit more complicated and also takes quite some effort on the database adapting/extending the PL/SQL routines.
Community
  • 1
  • 1
chiccodoro
  • 14,407
  • 19
  • 87
  • 130
  • @Benny: The issue exists for ans LOBs, hence the referenced BLOB postings, but I struggled with CLOBs. I can't find any typo in my post... – chiccodoro Sep 15 '10 at 06:27
  • `MyXML := '{0}';` the `string.Format()` is replacing `'{0}'` with the xml - however, `'...'` is a string literal which is of the `VARCHAR2` datatype (not a `CLOB`). [PL/SQL has a 32k limit](http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/datatypes.htm#i43252) on `VARCHAR2`s which is why this is failing. – MT0 Jul 12 '16 at 08:18

4 Answers4

15

I found that there is another way to work around the problem! My fellow employee saved my day pointing me to this blog, which says:

Set the parameter value when BeginTransaction has already been called on the DbConnection.

Could it be simpler? The blog relates to Oracle.DataAccess, but it works just as well for System.Data.OracleClient.

In practice this means:

varcmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;

var xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);

// DO NOT assign the parameter value yet in this place

cmd.Transaction = _oracleConnection.BeginTransaction();
try
{
    // Assign value here, AFTER starting the TX
    xmlParam.Value = xmlWithWayMoreThan4000Characters;

    cmd.ExecuteNonQuery();
    cmd.Transaction.Commit();
}
catch (OracleException)
{
    cmd.Transaction.Rollback();
}
chiccodoro
  • 14,407
  • 19
  • 87
  • 130
  • Does this approach also work around the 32k limitation? Or just the 4k limit? – mikemanne Jun 07 '12 at 15:22
  • I'm afraid I can't tell you this out of my mind anymore - I probably could if you asked me 1.5 years ago... I think to have in mind that it works around the 32k limit, too. Can you please let me know once you verified/falsified it? – chiccodoro Jun 08 '12 at 19:41
  • will definitely post, if we get a chance to confirm/falsify. Gotta love resurrecting old questions, eh? :) – mikemanne Jun 11 '12 at 13:35
  • 2
    my opinion on old question is that there are no old questions. This is not a forum but a Q/A site, so ideally a specific question is found exactly once and the answers are attached to it. – chiccodoro Jun 12 '12 at 10:47
  • 1
    I've just tried this with a 40k character string and it works fine. – Neil Vass Dec 10 '12 at 12:54
  • 2
    Updated link: http://henbo.wordpress.com/2007/12/05/ora-01460-unimplemented-or-unreasonable-conversion-requested/ – Samah Apr 17 '13 at 00:09
  • Worked for me on .NET 4.5 and Oracle 12c uploading close to 1MB of XML – wweicker Jan 26 '16 at 20:44
3

In my case, chiccodoro's solution did not work. I'm using ODP.NET ( Oracle.DataAccess ).

For me the solution is using OracleClob object.

OracleCommand cmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;

OracleParameter xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);

//connection should be open!
OracleClob clob = new OracleClob(_oracleConnection);
// xmlData: a string with way more than 4000 chars
clob.Write(xmlData.ToArray(),0,xmlData.Length);
xmlParam.Value = clob; 

try
{
    cmd.ExecuteNonQuery();
}
catch (OracleException e)
{
}
  • Hi Kemalettin, thank you for sharing. The original question was about `System.Data.OracleClient` but since the same issue exists for `Oracle.DataAccess` this will certainly be interesting to some readers. – chiccodoro Jul 09 '13 at 06:40
  • Even this solution wasn't enough in my case. I had to write the data in the clob by chunks of 1 ko, using a loop and the `clob.Append` method – Kevin Gosse Aug 26 '13 at 15:06
1

chiccodoro is right.

public static int RunProcedure(string storedProcName, IDataParameter[] parameters)
    {
        using (OracleConnection connection = new OracleConnection(connectionString))
        {
            int rowsAffected;

            OracleCommand command = new OracleCommand(storedProcName, connection);
            command.CommandText = storedProcName;
            command.CommandType = CommandType.StoredProcedure;
            foreach (OracleParameter parameter in parameters)
            {
                command.Parameters.Add(parameter);
            }
            connection.Open();

            try
            {
                // start transaction
                command.Transaction = connection.BeginTransaction();
                rowsAffected = command.ExecuteNonQuery();
                command.Transaction.Commit();
            }
            catch (System.Exception ex)
            {
                command.Transaction.Rollback();
                throw ex;
            }

            connection.Close();
            return rowsAffected;
        }
    }
Vincent
  • 101
  • 1
  • 2
  • 1
    thank you for confirming :-). You have posted quite some code. Maybe you could expand a little bit on what the difference of your code to my code is or as what you intended to say by posting it? – chiccodoro Jul 09 '13 at 06:42
1

I guess I just googled this for you to get cheap points, but there's a great explanation here:

http://www.orafaq.com/forum/t/48485/0/

Basically you cannot use more than 4000 chars in a string literal, and if you need to do more, you must use a stored procedure. Then, you are limited to 32KB at max so you have to "chunk" the inserts. Blech.

-Oisin

Matthew Whited
  • 22,160
  • 4
  • 52
  • 69
x0n
  • 51,312
  • 7
  • 89
  • 111
  • Hi xOn, you more or less repeated what I've already written in my question, haven't you? - Or does that mean in fact you'd confirm I don't have any other options? – chiccodoro Aug 25 '10 at 06:48
  • 1
    @chiccodoro - yes, i am confirming that you are out of luck, afaict. – x0n Aug 25 '10 at 15:15