oci関数を使用して、特定のOracleデータベースのさまざまなスキーマからデータを取得するために作成したPHPスクリプト(以下にコピー)を使用しています。昨日、これまでのところ 100% 再現可能であり、理解するのが難しい問題に気付きました。
問題は次のとおりです。単純なクエリ ("select * from table where id = '42'") と 2 つのスキーマ S1 と S2 があります。最初に DB に接続し、次にスキーマごとに current_schema を変更してクエリを実行します。S1でこれを行うと、0の結果が返されますが、問題ありません。S2でこれを行うと、2つの結果が返されますが、問題ありません。これを S1 で実行してから S2 で実行すると、S1 で 0 の結果が返され (問題ありません)、
1858 - ORA-01858: a non-numeric character was found where a numeric was expected
S2 でクエリを実行しようとするとエラーが発生します。対応する非数字文字は、「select *」の「*」のようです。
Oracle キャッシュの問題を疑っていたので、クエリを実行する前にクエリの最後にランダムなコメントを追加することにしました。これで問題は解決します。これはかなりハックであり、パフォーマンス上の理由から間違っている可能性があります。
SQL開発者でこの問題を再現できなかったため、問題はコード自体に起因する可能性もありますが、ランダムなサフィックスが問題を修正するという事実は、Oracleキャッシュが間違ったことをしていると考えさせます.
だからここに私のコードがあります:最初の部分にはoci関数をラップする便利な関数が含まれています.2番目の部分はいくつかの異なるテストの実際の実行です.BUGはバグのあるシナリオ(ORA-01858)、RANDはランダムな接尾辞を持つシナリオです、SCHEMA_1 は最初のスキーマのみのシナリオ (結果 0 : 正常)、SCHEMA_2 は 2 番目のスキーマのみのシナリオ (結果 2 : 正常)、SAME_SCHEMA は 2 番目のスキーマが 2 回のシナリオ (結果 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 コードを更新して、ランダム トークンに基づいて sql_id を取得しselect * from v$sql where sql_text like '%random_part'
、 でクエリ プランを取得できるようにしましたselect * from table(dbms_xplan.display_cursor('sql_id'))
。「RAND」シナリオでは、このランダム部分は選択クエリを 2 回実行するたびに異なります。他のシナリオでは、ランダム部分はクエリの異なる実行に対して保持されます。
次に、次を取得します。
- RAND シナリオの場合
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 (1 と 0 に対応)、PARSE_CALLS (1 と 0 に対応)、BUFFER_GETS (4 と 3 に対応)、OPTIMIZER_COST (2 と 1 に対応)、PARSING_SCHEMA_ID (193 と 203 に対応)、PARSING_SCHEMA_NAME (それぞれ S1 と S2)、CHILD_NUMBER (それぞれ 0 と 1)、CPU_TIME (それぞれ 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')
同じクエリプランが使用されているようです。