sqloracle-databaseodp.netora-00600

passing associative array of type Timestamp to oracle stored procedure


We're running into a strange error using Oracle Odp.Net (connecting to Oracle 9). The problem is illustrated by the code snippet below.

This is the error we're experiencing:

ORA-00600: internal error code, arguments: [15419], [severe error during PL/SQL execution], [], [], [], [], [], []

ORA-06544: PL/SQL: internal error, arguments: [78502], [], [], [], [], [], [], []

ORA-06553: PLS-801: internal error [78502]

Googling around makes us suspect (though we're not entirely sure) that passing an array of Timestamps is not supported by Odp.Net.

So the question is 2-fold:

C# console program illustrating the problem:

using System;
using System.Collections;
using System.Data;
using Oracle.DataAccess.Client;

class Program 
{
private const string _db = "<db>";
private const string _username = "<user>";
private const string _password = "<password>";
private const string _storedProcedureName = "<sproc>";

static void Main(string[] args)
{
  var connectionString = string.Format(
                              "data source={0};user id={1};password={2}", 
                              _db, _username, _password);

  var connection = new OracleConnection(connectionString);

  try
  {

    connection.Open();


    var timeStamps = new[] { DateTime.Now, DateTime.Now };

    var parameter = new OracleParameter("inTimeStamps", OracleDbType.TimeStamp)
      {
        Direction = ParameterDirection.Input,
        CollectionType = OracleCollectionType.PLSQLAssociativeArray,
        Size = timeStamps.Length,
        Value = timeStamps
      };

    var command = connection.CreateCommand();
    command.CommandType = CommandType.StoredProcedure;
    command.CommandText = _storedProcedureName;
    command.Parameters.Add(parameter);

    command.ExecuteReader();

  }
  finally
  {
    connection.Close();
  }
}
}

The code is calling the following PL/SQL stored procedure

  TYPE ArrayOfTimestamps is table of timestamp index by binary_integer;

  PROCEDURE TestOdpTimeStamp (inTimeStamps in ArrayOfTimestamps)
  IS
  test number;
  BEGIN
     select 1 into test from dual;
  END;

Solution

  • You can pass a nested table of timestamps instead of an associative array to a PL/SQL procedure.

    You need odp.net 11.1.0.6.20 or higher, you can connect with odp.net 11.1.0.6.20 to an Oracle 9 server.

    Execute as Oracle user testts:

    create or replace type MyTimeStamp as object 
    (
      my timestamp
    )
    /
    
    create or replace type mytimestamp_table as table of MyTimeStamp 
    /
    
    create table testinserttimestamp 
    ( my timestamp);
    
    create or replace procedure test_timestamp_table (p_in in mytimestamp_table)
    is
    begin
      for i in p_in.first..p_in.last loop
        insert into testinserttimestamp values (p_in(i).my);
     end loop;
     commit;
    end;
    

    In C#, create a form with a button called button1, and do...

    using System;
    using System.Data;
    using System.Windows.Forms;
    using Oracle.DataAccess.Client;
    using Oracle.DataAccess.Types;
    
    namespace TestTimeStamp
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
        }
    
    
        public class MyUdtTimeStamp : INullable, IOracleCustomType
        {
    
          [OracleObjectMappingAttribute("MY")]
          public OracleTimeStamp My { get; set; }
    
          public bool IsNull
          {
            get { return false;}
          }
    
          public void FromCustomObject(OracleConnection con, IntPtr pUdt)
          {
            OracleUdt.SetValue(con, pUdt, "MY", My);
          }
    
          public void ToCustomObject(OracleConnection con, IntPtr pUdt)
          {
            My = (OracleTimeStamp)OracleUdt.GetValue(con, pUdt, "MY");
          }
        }
    
        [OracleCustomTypeMappingAttribute("TESTTS.MYTIMESTAMP")]
        public class StudentFactory : IOracleCustomTypeFactory
        {
          public IOracleCustomType CreateObject()
          {
            return new MyUdtTimeStamp();
          }
        }
    
        [OracleCustomTypeMappingAttribute("TESTTS.MYTIMESTAMP_TABLE")]
        public class PersonArrayFactory : IOracleArrayTypeFactory
        {
          public Array CreateArray(int numElems)
          {
            return new MyUdtTimeStamp[numElems];
          }
    
          public Array CreateStatusArray(int numElems)
          {
            return null;
          }
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
          OracleConnectionStringBuilder b = new OracleConnectionStringBuilder();
          b.UserID = "testts";
          b.Password = "ts";
          b.DataSource = "ora11";
          using (OracleConnection conn = new OracleConnection(b.ToString())) {
            conn.Open();
            using (OracleCommand comm = conn.CreateCommand())
            {
              comm.CommandText = "begin test_timestamp_table(:1); end;";
              OracleParameter p = new OracleParameter();
              p.OracleDbType = OracleDbType.Array;
              p.Direction = ParameterDirection.Input;
    
              p.UdtTypeName = "TESTTS.MYTIMESTAMP_TABLE";
              MyUdtTimeStamp[] times = new MyUdtTimeStamp[2];
              MyUdtTimeStamp m1 = new MyUdtTimeStamp();
              m1.My = new OracleTimeStamp(DateTime.Now);
              MyUdtTimeStamp m2 = new MyUdtTimeStamp();
              m2.My = new OracleTimeStamp(DateTime.Now);
              times[0] = m1;
              times[1] = m2;
              p.Value = times;
    
              comm.Parameters.Add(p);
    
              comm.ExecuteNonQuery();
            }
    
            conn.Close();
          }
        }
      }
    }
    

    Do in Oracle...

    SQL> select * from testinserttimestamp;
    
    MY
    -------------------------------------------------
    12-10-09 21:13:54,328125
    12-10-09 21:13:55,171875