0

I have developed an application that creates tables based on external definitions. If these definitions change, the application drops and recreates the tables affected. It has to do that while the connection is opened because that task is part of a group of tasks. However, if I query a table that has changed with "select *" afterwards, I get the columns of the old definition. If columns have been added to the table, "select *" does not see it. Here is a sample code that demonstrates this:

private static void OraSelectStarProblem()
{
    var tabDef1 = "CREATE TABLE TEMP_PS_000 (SESSIONID RAW(16), EXECNR NUMBER, DUEDATE DATE)";
    //var tabDef2 = "CREATE TABLE TEMP_PS_000 (YASESSIONID RAW(16), YAEXECNR NUMBER, YADUEDATE DATE, NEWFIELD VARCHAR2(100))";
    var tabDef2 = "CREATE TABLE TEMP_PS_000 (YADUEDATE DATE, NEWFIELD VARCHAR2(100), SOMEOTHER integer)"; // causes ORA-00932 when querying
    var selectCmd = "select * from TEMP_PS_000";
    using (var conn = new OracleConnection(GetConnStr()))
    {
        conn.Open();
        //PerformSelectStar(selectCmd, conn);
        DropTempTable(conn);
        Console.Write("Creating TEMP_PS_000 with 3 columns... ");
        using (var cmd = conn.CreateCommand())
        {
            cmd.CommandText = tabDef1;
            cmd.ExecuteNonQuery();
        }
        Console.WriteLine("Done!");
        PerformSelectStar(selectCmd, conn);
        //conn.PurgeStatementCache(); // no effect
        //conn.FlushCache(); // no effect
        /*Console.WriteLine("Resetting connection");
        conn.Close();
        OracleConnection.ClearPool (conn);
        conn.Open();*/
        DropTempTable(conn);
        //conn.PurgeStatementCache(); // no effect
        //conn.FlushCache(); // no effect
        Console.Write("Creating TEMP_PS_000 with 4 columns... ");
        using (var cmd = conn.CreateCommand())
        {
            cmd.CommandText = tabDef2;
            cmd.ExecuteNonQuery();
        }
        Console.WriteLine("Done!");
        PerformSelectStar(selectCmd, conn);
    }
}

private static void DropTempTable(OracleConnection conn)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = "begin drop_table('TEMP_PS_000'); end;";
        cmd.ExecuteNonQuery();
    }
}

private static void PerformSelectStar(string selectCmd, OracleConnection conn)
{
    try
    {
        using (var cmd = conn.CreateCommand())
        {
            cmd.CommandText = selectCmd;
            var dataAdapter = new OracleDataAdapter(cmd);
            var dataSet = new DataSet();
            dataAdapter.Fill(dataSet);
            var columns = "";
            foreach (DataColumn column in dataSet.Tables[0].Columns)
            {
                columns += column.ColumnName + " ";
            }
            Console.WriteLine("Columns: " + columns);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("PerformSelectStar: " + ex.Message);
    }
}

Software versions used: .net 4.5.1 and Oracle 12.1

If the part that resets the connection is activated, "select *" works, but then all data in temporary tables is flushed, which is not desired.

I would like to know if there is some method of telling either ODP.net or Oracle - whichever is the reason for this strange behaviour - that a table's definition has been changed and that it should reread the table definition prior to executing "select *".

Michael Kremser
  • 185
  • 1
  • 9
  • Why would you do this? Seems like it defies the point in a relational database system and you should look into a DocumentDB solution... If you do need to do this, use the metadata views provided by the database to get an explicit list of the columns and use that in the query instead of * – Milney Jan 23 '17 at 10:32
  • @Milney The data in those tables is used in conjunction with SQL queries on a database that is normalized. While putting it elsewhere would be possible, it would also cause the queries to get more complicated (i.e. the code would be much harder to read and maintain). – Michael Kremser Jan 23 '17 at 12:43
  • 1
    Problem could be solved: the trick is to slightly modify the statement in a way that doesn't affect the result, as shown here: http://stackoverflow.com/a/16861388/3424360 – Michael Kremser Jan 23 '17 at 12:46
  • yes that was what i meant by using the metadata views to get a list of the columns.... – Milney Jan 23 '17 at 13:10
  • what does the "drop_table" procedure look like? – tbone Jan 23 '17 at 14:38
  • @Milney I don't use the metadata, I just do something like this: 1) "select * from TEMP_PS_000 t123" before dropping/recreating and 2) "select * from TEMP_PS_000 t234" after that. This way, the Oracle DMBS reevaluates the column list itself, without the need of my program to do that (in fact it _is_ the DBMS's job to do that). – Michael Kremser Jan 23 '17 at 20:26
  • @tbone It merely looks if the table supplied exists and drops it on demand. Unfortunately (and incomprehensible IMHO), Oracle DBMS still lacks a "if exists" clause in "drop table". The procedure is similar to this one: http://stackoverflow.com/a/1799215/3424360 – Michael Kremser Jan 23 '17 at 20:39
  • 1
    What happens if you use Metadata Pooling = false and Statement Cache Size = 0 in the connection string? See: https://docs.oracle.com/database/122/ODPNT/featConnecting.htm#GUID-0CFEB161-68EF-4BC2-8943-3BDFFB878602 – Christian Shay Jan 23 '17 at 20:48
  • 1
    Also try Self Tuning = False as it may create a statement cache for you if you don't. – Christian Shay Jan 23 '17 at 20:56

3 Answers3

1

Try setting Metadata Pooling = false, Self Tuning = False and Statement Cache Size = 0 in the connection string.

https://docs.oracle.com/database/122/ODPNT/featConnecting.htm#GUID-0CFEB161-68EF-4BC2-8943-3BDFFB878602

Christian Shay
  • 2,570
  • 14
  • 24
0

It looks like this is some sort of a caching problem, either of ODP.net or Oracle DBMS itself. However, if the second statement is slightly different than the first one, caching can be circumvented. The code above can be modified in a way that aliases are assigned to the table which doesn't affect the result but makes the DBMS thinking it would receive a "new" statement:

...
PerformSelectStar(selectCmd + " alias1", conn);
...
PerformSelectStar(selectCmd + " alias2", conn);
...

Of course, that's just a workaround. A real solution would be to tell the DBMS that caching is not desired.

Michael Kremser
  • 185
  • 1
  • 9
  • See my comment in your question above for suggestions on how to turn off MetaData Pooling and Statement Caching. – Christian Shay Jan 23 '17 at 21:05
  • @ChristianShay Your hints in the comments above directed in the right direction: adding "Metadata Pooling=false;Self Tuning=false;" to the connection string solves the problem. However, I have to find out if there are any unwanted side effects, especially concerning query performance. – Michael Kremser Jan 24 '17 at 08:04
-1

Try modifying the tables within a transaction. Commit before your select.

  • In Oracle, CREATE TABLE and ALTER TABLE are not aware of transactions, i.e. they happen immediately. However, I tried to wrap the statements in transactions, but the behaviour is the same. – Michael Kremser Jan 23 '17 at 12:31