与Oracle / oci_connect / PHP(缓存?)有奇怪的ORA-01858异常

时间:2012-09-19 23:18:37

标签: php oracle caching oci

我正在使用PHP脚本(下面复制)我编写了使用oci函数从给定Oracle数据库的不同模式中检索数据。昨天,我注意到一个问题,到目前为止100%可重复,我很难理解。

问题在于:我有一个简单的查询("从表中选择*,其中id =' 42'")和两个模式S1和S2。首先我连接到DB,然后对于每个模式,我更改current_schema并执行查询。 如果我在S1上执行此操作,则返回0结果,这很好。 如果我在S2上执行此操作,则返回2个结果,这很好。 如果我在S1上执行此操作,那么S2,它在S1上返回0结果(这很好),我得到了一个 尝试在S2上执行查询时出现1858 - ORA-01858: a non-numeric character was found where a numeric was expected错误。相应的非数字字符似乎是' *'来自"选择*"。

由于我怀疑Oracle缓存问题,我决定在执行之前在查询末尾附加一个随机注释。这确实解决了这个问题。 这很糟糕,出于表演原因可能会出错。

由于我无法在SQL开发人员中重现此问题,问题可能也来自我的代码本身,但随机后缀修复问题的事实让我认为Oracle缓存正在做错事。

所以这里是我的代码:第一部分包含用于包装oci函数的有用函数,第二部分是几个不同测试的实际运行:BUG是错误情形(ORA-01858),RAND是具有随机后缀(精细)的场景,SCHEMA_1是仅具有第一个架构的场景(0结果:罚款),SCHEMA_2是仅具有第二个架构的场景(2个结果:罚款),SAME_SCHEMA是具有第二个架构两次的场景( 2结果两次:罚款)。

<?php

function db_trigger_error($e) // http://php.net/manual/en/function.oci-error.php
{
    $message =
        "\n<pre>\n" .
        htmlentities($e['code']) . " - " .
        htmlentities($e['message']) . " : \n" .
        htmlentities(str_replace(array("\r\n","\t"),array("  "," "),$e['sqltext'])) .
        sprintf("\n%" . ($e['offset']+1) . "s", "^") .
        "\n</pre>\n";
    throw new Exception($message);
}

function execute_query($conn,$query,$fetch_wanted,&$fetch,&$result)
{
    echo($query . "; ...<br>\n");

    // PARSE
    $stid = oci_parse($conn, $query);
    if (!$stid) { db_trigger_error(oci_error($conn)); }
    $type = oci_statement_type($stid); 

    // EXECUTE
    $exe = oci_execute($stid);
    if (!$exe) { db_trigger_error(oci_error($stid)); }

    // FETCH ARRAY RESULT
    $update = oci_num_rows($stid);
    $fetch = $fetch_wanted  ?
        oci_fetch_all($stid, $result,null,null,OCI_FETCHSTATEMENT_BY_COLUMN) :
        0;

    // FREE STUFF
    oci_free_statement($stid);
    echo(" ... returned $fetch row" . ($fetch>1?'s':'') . " and updated/inserted/deleted $update row" . ($update>1?'s':'') . " ($type)<br>\n");
}

function connect_db(&$conn,$username,$password,$connec_string)
{   
    echo("Connecting to username/password@connec_string<br>\n");
    $conn = oci_connect($username,$password,$connec_string);
    if (!$conn) { db_trigger_error(oci_error()); }
    echo("Connected to Server " . oci_server_version($conn) . " with Client version " . oci_client_version($conn) . "<br>\n");
}

function close_connect_db($conn)
{
    if ($conn)
    {
        echo("Connection closed<br>\n");
        oci_close($conn);
    } else 
    {
        echo("No connection to close<br>\n");
    }
}


$username="USERNAME";
$password="PASSWORD";
$connec_string="(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=HOST)(PORT=PORT)))(CONNECT_DATA=(SERVICE_NAME=SERVICE)))";
$query="select * from TABLE t where t.T_ID = '035DA31100000000'";

foreach(array('BUG','RAND','SCHEMA_1','SCHEMA_2','SAME_SCHEMA') as $testCase)
{
    echo "Start $testCase<br>\n";
    $schemas=($testCase=='SCHEMA_1') ?    array('S1') :
             ($testCase=='SCHEMA_2') ?    array('S2') :   
             ($testCase=='SAME_SCHEMA') ? array('S2','S2') :
                                          array('S1','S1');

    connect_db($conn,$username, $password, $connec_string);
    foreach($schemas as $schema)
    {
        $suffix=($testCase=='RAND') ? ' --' . rand() : '';
        $query_schema="alter session set current_schema=$schema";
        try 
        {
            execute_query ($conn,$query_schema,   false,$fetch,$result,false);
            execute_query($conn, $query . $suffix,true, $fetch,$result,false);
        }  catch (Exception $e)
        {
            echo 'Caught exception: ',  $e->getMessage(), "\n";
            $fetch=0;
        }
    }
    close_connect_db($conn);
    echo "End $testCase<br>\n";
}
?>

相应的输出是:

Start BUG
Connecting to username/password@connec_string
Connected to Server Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production With the Partitioning and Data Mining options with Client version 10.2.0.3.0
alter session set current_schema=S1; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 0 row and updated/inserted/deleted 0 row (SELECT)
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...

Warning: oci_execute() [function.oci-execute]: ORA-01858: a non-numeric character was found where a numeric was expected in /users/myself/public_html/ProofOfConcept.php on line 25
Caught exception:
1858 - ORA-01858: a non-numeric character was found where a numeric was expected : 
select * from TABLE t where t.T_ID = '035DA31100000000'
       ^
Connection closed
End BUG

Start RAND
Connecting to username/password@connec_string
Connected to Server Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production With the Partitioning and Data Mining options with Client version 10.2.0.3.0
alter session set current_schema=S1; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000' --1458191218; ...
... returned 0 row and updated/inserted/deleted 0 row (SELECT)
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000' --849888503; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
Connection closed
End RAND

Start SCHEMA_1
Connecting to username/password@connec_string
Connected to Server Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production With the Partitioning and Data Mining options with Client version 10.2.0.3.0
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
Connection closed
End SCHEMA_1

Start SCHEMA_2
Connecting to username/password@connec_string
Connected to Server Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production With the Partitioning and Data Mining options with Client version 10.2.0.3.0
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
Connection closed
End SCHEMA_2

Start SAME_SCHEMA
Connecting to username/password@connec_string
Connected to Server Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production With the Partitioning and Data Mining options with Client version 10.2.0.3.0
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
alter session set current_schema=S2; ...
... returned 0 row and updated/inserted/deleted 0 row (ALTER)
select * from TABLE t where t.T_ID = '035DA31100000000'; ...
... returned 2 rows and updated/inserted/deleted 0 row (SELECT)
Connection closed
End SAME_SCHEMA

有人知道这个问题吗?如果在DB端有适当的修复?使用随机评论作为后缀不好吗? 提前谢谢。

编辑以添加部分调查

这是在Justin Cave的良好建议下完成的。

我现在已经更新了PHP代码,以便我可以使用select * from v$sql where sql_text like '%random_part'基于随机令牌检索sql_id,以便我可以使用select * from table(dbms_xplan.display_cursor('sql_id'))获取查询计划。在&#39; RAND&#39;方案,这个随机部分是不同的,我们运行select查询的两次,在其他情况下,随机部分保留用于查询的不同运行。

然后我们得到:

  • 对于兰德情景

select * from v$sql where sql_text like '%random_part'返回1行。 相应的查询计划是

        Plan hash value: 2841037776

  ----------------------------------------------------------------------------------------------------------
  | Id  | Operation                   | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
  ----------------------------------------------------------------------------------------------------------
  |   0 | SELECT STATEMENT            |                            |       |       |     1 (100)|          |
  |   1 |  TABLE ACCESS BY INDEX ROWID| TABLE                      |     1 |   283 |     1   (0)| 00:00:01 |
  |*  2 |   INDEX RANGE SCAN          | I_T_ID                     |     1 |       |     1   (0)| 00:00:01 |
  ----------------------------------------------------------------------------------------------------------

   Predicate Information (identified by operation id):             
   ---------------------------------------------------

       2 - access("BFP"."T_ID"='035DA31100000000')
  • 对于越野车场景:

select * from v$sql where sql_text like '%random_part'返回具有相同SQL_ID的2行。行的不同部分是:END_OF_FETCH_COUNT(resp 1和0),PARSE_CALLS(resp 1和0),BUFFER_GETS(resp 4和3),OPTIMIZER_COST(resp 2和1),PARSING_SCHEMA_ID(resp 193和203),PARSING_SCHEMA_NAME( resp S1和S2),CHILD_NUMBER(resp 0和1),CPU_TIME(resp 7598和4093),ELAPSED_TIME(相同),CHILD_ADDRESS(奇怪的事情)。 相应的查询计划是

 Plan hash value: 2841037776

  ----------------------------------------------------------------------------------------------------------
  | Id  | Operation                   | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
  ----------------------------------------------------------------------------------------------------------
  |   0 | SELECT STATEMENT            |                            |       |       |     2 (100)|          |
  |   1 |  TABLE ACCESS BY INDEX ROWID| TABLE                      |     5 |  1535 |     2   (0)| 00:00:01 |
  |*  2 |   INDEX RANGE SCAN          | I_T_ID                     |     5 |       |     1   (0)| 00:00:01 |
  ----------------------------------------------------------------------------------------------------------

   Predicate Information (identified by operation id):             
   ---------------------------------------------------

       2 - access("BFP"."T_ID"='035DA31100000000')

似乎使用了相同的查询计划。

0 个答案:

没有答案