Sometimes you get the error "ORA-01461: can bind a LONG value only for insert into a LONG column". This error is highly misleading especially when you have no LONG columns or LONG values.
From my testing it seems this error can be caused when the value of a bound variable exceeds the length allocated.
To avoid this error make sure you specify lengths when binding varchars e.g.
<?php
oci_bind_by_name($stmt,':string',$string, 256);
?>
And for numerics use the default length (-1) but tell oracle its an integer e.g.
<?php
oci_bind_by_name($stmt,':num',$num, -1, SQLT_INT);
?>
oci_bind_by_name
(PHP 5, PECL OCI8 >= 1.1.0)
oci_bind_by_name — Oracle プレースホルダに PHP 変数をバインドする
説明
$statement
, string $bv_name
, mixed &$variable
[, int $maxlength = -1
[, int $type = SQLT_CHR
]] )
PHP の変数
variable を Oracle プレースホルダ
bv_name にバインドします。
バインドは Oracle データベースのパフォーマンスに影響を及ぼす重要な仕組みで、
SQL インジェクション攻撃を防ぐための方法にもなります。
バインドを使うとデータベースでステートメントのコンテキストを (たとえ別のユーザーやプロセスが実行したものであっても) 再利用することができるようになります。 バインドによって SQL インジェクションの心配を軽減できる理由は、 バインド変数で代入したデータが決して SQL 文の一部と見なされることがないからです。 クォートやエスケープは不要です。
バインドする PHP 変数の中身を変更してステートメントを再実行するときにも、 ステートメントをパースしなおしたり変数をバインドしなおしたりする必要はありません。
Oracle のバインド変数は、データベースに渡す値に使う IN 変数と PHP に返される値に使う OUT 変数のふたつに大別できます。 バインド変数は IN あるいは OUT のどちらかの形式になります。バインド変数を入力用に使うか出力用に使うかは、 実行時に決まります。
OUT 変数を使う場合は
maxlength を指定しなければなりません。
これを用いて、返される値を格納するのに必要なメモリを PHP が確保します。
IN 変数の場合も、もし PHP
の変数にさまざまな値を格納してステートメントを何度も実行するのであれば
maxlength を設定しておくことを推奨します。
そうしないと、最初に渡した PHP の変数の値にあわせて
Oracle がデータの長さを切り詰めてしまう可能性があります。
最大長がどの程度になるかわからない場合は現在のデータサイズで
oci_bind_by_name()
を再コールしてから oci_execute() を実行するようにしましょう。
不必要に大きなサイズでバインドすると、
データベースのプロセスのメモリ使用量に影響を及ぼします。
バインドは、Oracle にデータをどのメモリアドレスから読み込むのかを指示します。 IN 変数の場合、 oci_execute() がコールされたときにそのメモリアドレスに正しいデータがなければなりません。 つまり、バインドされる変数は実行時までスコープ内に残っていなければならないということです。 もしそうでなければ、 "ORA-01460: unimplemented or unreasonable conversion requested" のような予期せぬエラーが発生します。 OUT 変数の場合は、PHP の変数に何も値が格納されないといったことになるでしょう。
繰り返し実行されるステートメントで決して値が変わることのないバインド変数を使っていると、 Oracle のオプティマイザが最適な実行プランを選びにくくなる可能性があります。 実行に長い時間がかかり、再実行されることもめったにないようなステートメントは、 バインドの恩恵を受けられないでしょう。 しかし、どちらの場合であっても、文字列を連結して SQL 文を作るよりはバインドを使ったほうが安全です。 ユーザーの入力をフィルタリングせずに埋め込んでしまうというリスクをなくせるからです。
パラメータ
-
statement -
有効な OCI8 ステートメント識別子。
-
bv_name -
コロンを先頭につけたバインド変数プレースホルダをステートメント内で使います。 コロンは
bv_nameでは必須ではありません。 Oracle はクエスチョンマークをプレースホルダとして使いません。 -
variable -
bv_nameに関連づける PHP の変数。 -
maxlength -
バインド時の最大長。-1 に設定した場合、
variableの現在の長さを最大長として設定します。 この場合は、 oci_bind_by_name() がコールされたときにvariableが存在してデータが格納されている必要があります。 -
type -
Oracle がデータを扱うときのデータ型。デフォルトの
typeはSQLT_CHRです。 Oracle は、可能な場合はこの型とデータベースのカラム (あるいは PL/SQL の変数) の型の間で変換を行います。抽象データ型 (LOB/ROWID/BFILE) をバインドする必要がある場合、まず oci_new_descriptor() 関数を使用してこれを確保する必要があります。
lengthは抽象データ型用には 使用されず、-1 を設定する必要があります。typeに設定できる値は以下のとおりです。-
SQLT_BFILEEorOCI_B_BFILE- BFILE 用 -
SQLT_CFILEEorOCI_B_CFILEE- CFILE 用 -
SQLT_CLOBorOCI_B_CLOB- CLOB 用 -
SQLT_BLOBorOCI_B_BLOB- BLOB 用 -
SQLT_RDDorOCI_B_ROWID- ROWID 用 -
SQLT_NTYorOCI_B_NTY- 名前付けされたデータ型用 -
SQLT_INTorOCI_B_INT- integer 用 -
SQLT_CHR- VARCHAR 用 -
SQLT_BINorOCI_B_BIN- RAW カラム用 -
SQLT_LNG- LONG カラム用 -
SQLT_LBI- LONG RAW カラム用 -
SQLT_RSET- カーソル用。 oci_new_cursor() により、前もって生成されたもの。
-
返り値
成功した場合に TRUE を、失敗した場合に FALSE を返します。
例
例1 oci_bind_by_name() によるデータの挿入
<?php
// このようなテーブルを作ります
// CREATE TABLE mytab (id NUMBER, text VARCHAR2(40));
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$stid = oci_parse($conn,"INSERT INTO mytab (id, text) VALUES(:id_bv, :text_bv)");
$id = 1;
$text = "Data to insert ";
oci_bind_by_name($stid, ":id_bv", $id);
oci_bind_by_name($stid, ":text_bv", $text);
oci_execute($stid);
// テーブルに入る内容: 1, 'Data to insert '
?>
例2 一度組み立てたものを複数回実行
<?php
// このようなテーブルを作ります
// CREATE TABLE mytab (id NUMBER);
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$a = array(1,3,5,7,11); // 挿入するデータ
$stid = oci_parse($conn, 'INSERT INTO mytab (id) VALUES (:bv)');
oci_bind_by_name($stid, ':bv', $v, 20);
foreach ($a as $v) {
$r = oci_execute($stid, OCI_DEFAULT); // 自動コミットはしません
}
oci_commit($conn); // すべてをここでコミットします
// テーブルには次の 5 行が含まれます: 1, 3, 5, 7, 11
oci_free_statement($stid);
oci_close($conn);
?>
例3 foreach() ループでのバインド
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$sql = 'SELECT * FROM departments WHERE department_name = :dname AND location_id = :loc';
$stid = oci_parse($conn, $sql);
$ba = array(':dname' => 'IT Support', ':loc' => 1700);
foreach ($ba as $key => $val) {
// oci_bind_by_name($stid, $key, $val) ではうまくいきません。
// これは、すべてのプレースホルダを同じ内容にバインドしてしまうからです。
// $val ではなく、データの実際の位置を表す $ba[$key] を使いましょう。
oci_bind_by_name($stid, $key, $ba[$key]);
}
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);
foreach ($row as $item) {
print $item."<br>\n";
}
oci_free_statement($stid);
oci_close($conn);
?>
例4 WHERE 句でのバインド
<?php
$conn = oci_connect("hr", "hrpwd", "localhost/XE");
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$sql = 'SELECT last_name FROM employees WHERE employee_id = :eidbv';
$stid = oci_parse($conn, $sql);
$myeid = 101;
oci_bind_by_name($stid, ':eidbv', $myeid);
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC);
echo $row['LAST_NAME'] ."<br>\n";
// 出力は
// Kochhar
oci_free_statement($stid);
oci_close($conn);
?>
例5 LIKE 句でのバインド
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
// 'South' からはじまるすべての都市を探します
$stid = oci_parse($conn, "SELECT city FROM locations WHERE city LIKE :bv");
$city = 'South%'; // '%' は SQL でのワイルドカードです
oci_bind_by_name($stid, ":bv", $city);
oci_execute($stid);
oci_fetch_all($stid, $res);
foreach ($res['CITY'] as $c) {
print $c . "<br>\n";
}
// 出力は
// South Brunswick
// South San Francisco
// Southlake
oci_free_statement($stid);
oci_close($conn);
?>
例6 REGEXP_LIKE でのバインド
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
// 'ing' を含むすべての都市を探します
$stid = oci_parse($conn, "SELECT city FROM locations WHERE REGEXP_LIKE(city, :bv)");
$city = '.*ing.*';
oci_bind_by_name($stid, ":bv", $city);
oci_execute($stid);
oci_fetch_all($stid, $res);
foreach ($res['CITY'] as $c) {
print $c . "<br>\n";
}
// 出力は
// Beijing
// Singapore
oci_free_statement($stid);
oci_close($conn);
?>
IN 句の中の条件数が少なくて固定なのであれば、個々にバインド変数を割り当てればいいでしょう。 実行時に不明な値は NULL が設定されます。 こうすればひとつのステートメントをすべてのユーザーで使い回すことができ、 Oracle の DB キャッシュの効率を最大化させることができます。
例7 IN 句での複数の値のバインド
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$sql = 'SELECT last_name FROM employees WHERE employee_id in (:e1, :e2, :e3)';
$stid = oci_parse($conn, $sql);
$mye1 = 103;
$mye2 = 104;
$mye3 = NULL; // pretend we were not given this value
oci_bind_by_name($stid, ':e1', $mye1);
oci_bind_by_name($stid, ':e2', $mye2);
oci_bind_by_name($stid, ':e3', $mye3);
oci_execute($stid);
oci_fetch_all($stid, $res);
foreach ($res['LAST_NAME'] as $name) {
print $name ."<br>\n";
}
// 出力は
// Ernst
// Hunold
oci_free_statement($stid);
oci_close($conn);
?>
例8 クエリが返す ROWID のバインド
<?php
// このようなテーブルを作ります
// CREATE TABLE mytab (id NUMBER, salary NUMBER, name VARCHAR2(40));
// INSERT INTO mytab (id, salary, name) VALUES (1, 100, 'Chris');
// COMMIT;
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$stid = oci_parse($conn, 'SELECT ROWID, name FROM mytab WHERE id = :id_bv FOR UPDATE');
$id = 1;
oci_bind_by_name($stid, ':id_bv', $id);
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);
$rid = $row['ROWID'];
$name = $row['NAME'];
// 名前を大文字に変換してそれを保存します
$name = strtoupper($name);
$stid = oci_parse($conn, 'UPDATE mytab SET name = :n_bv WHERE ROWID = :r_bv');
oci_bind_by_name($stid, ':n_bv', $name);
oci_bind_by_name($stid, ':r_bv', $rid, -1, OCI_B_ROWID);
oci_execute($stid);
// テーブルに入る内容: 1, 100, CHRIS
oci_free_statement($stid);
oci_close($conn);
?>
例9 INSERT での ROWID のバインド
<?php
// この例では id と name を挿入し、それから salary を更新します
// このようなテーブルを作ります
// CREATE TABLE mytab (id NUMBER, salary NUMBER, name VARCHAR2(40));
//
// Based on original ROWID example by thies at thieso dot net (980221)
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$sql = "INSERT INTO mytab (id, name) VALUES(:id_bv, :name_bv)
RETURNING ROWID INTO :rid";
$ins_stid = oci_parse($conn, $sql);
$rowid = oci_new_descriptor($conn, OCI_D_ROWID);
oci_bind_by_name($ins_stid, ":id_bv", $id, 10);
oci_bind_by_name($ins_stid, ":name_bv", $name, 32);
oci_bind_by_name($ins_stid, ":rid", $rowid, -1, OCI_B_ROWID);
$sql = "UPDATE mytab SET salary = :salary WHERE ROWID = :rid";
$upd_stid = oci_parse($conn, $sql);
oci_bind_by_name($upd_stid, ":rid", $rowid, -1, OCI_B_ROWID);
oci_bind_by_name($upd_stid, ":salary", $salary, 32);
// 挿入する id と name
$data = array(1111 => "Larry",
2222 => "Bill",
3333 => "Jim");
// それぞれの salary
$salary = 10000;
// 挿入し、その後すぐに各行を更新します
foreach ($data as $id => $name) {
oci_execute($ins_stid);
oci_execute($upd_stid);
}
$rowid->free();
oci_free_statement($upd_stid);
oci_free_statement($ins_stid);
// 新しい行を表示します
$stid = oci_parse($conn, "SELECT * FROM mytab");
oci_execute($stid);
while ($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
var_dump($row);
}
oci_free_statement($stid);
oci_close($conn);
?>
例10 PL/SQL ストアドファンクションでのバインド
<?php
// PHP プログラムを実行する前に、ストアドファンクションを
// SQL*Plus あるいは SQL Developer で作ります
//
// CREATE OR REPLACE FUNCTION myfunc(p IN NUMBER) RETURN NUMBER AS
// BEGIN
// RETURN p * 3;
// END;
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}
$p = 8;
$stid = oci_parse($conn, 'begin :r := myfunc(:p); end;');
oci_bind_by_name($stid, ':p', $p);
// 返り値は OUT 変数に格納されます。デフォルトの型は文字列型なので、
// length が 40 ということは最大で 40 まで返される可能性があるということです
oci_bind_by_name($stid, ':r', $r, 40);
oci_execute($stid);
print "$r\n"; // 24 と表示します
oci_free_statement($stid);
oci_close($conn);
?>
例11 PL/SQL ストアドプロシージャでのパラメータのバインド
<?php
// PHP プログラムを実行する前に、ストアドプロシージャを
// SQL*Plus あるいは SQL Developer で作ります
//
// CREATE OR REPLACE PROCEDURE myproc(p1 IN NUMBER, p2 OUT NUMBER) AS
// BEGIN
// p2 := p1 * 2;
// END;
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}
$p1 = 8;
$stid = oci_parse($conn, 'begin myproc(:p1, :p2); end;');
oci_bind_by_name($stid, ':p1', $p1);
// プロシージャの 2 番目のパラメータは OUT 変数です。デフォルトの型は文字列型なので、
// length が 40 ということは最大で 40 まで返される可能性があるということです
oci_bind_by_name($stid, ':p2', $p2, 40);
oci_execute($stid);
print "$p2\n"; // 16 と表示します
oci_free_statement($stid);
oci_close($conn);
?>
例12 CLOB 列のバインド
<?php
// 実行する前にテーブルを作成します。
// CREATE TABLE mytab (mykey NUMBER, myclob CLOB);
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}
$mykey = 12343; // この例のための任意のキー
$sql = "INSERT INTO mytab (mykey, myclob)
VALUES (:mykey, EMPTY_CLOB())
RETURNING myclob INTO :myclob";
$stid = oci_parse($conn, $sql);
$clob = oci_new_descriptor($conn, OCI_D_LOB);
oci_bind_by_name($stid, ":mykey", $mykey, 5);
oci_bind_by_name($stid, ":myclob", $clob, -1, OCI_B_CLOB);
oci_execute($stid, OCI_DEFAULT);
$clob->save("A very long string");
oci_commit($conn);
// CLOB データを取得します
$query = 'SELECT myclob FROM mytab WHERE mykey = :mykey';
$stid = oci_parse ($conn, $query);
oci_bind_by_name($stid, ":mykey", $mykey, 5);
oci_execute($stid);
print '<table border="1">';
while ($row = oci_fetch_array($stid, OCI_ASSOC)) {
$result = $row['MYCLOB']->load();
print '<tr><td>'.$result.'</td></tr>';
}
print '</table>';
?>
返り値
成功した場合に TRUE を、失敗した場合に FALSE を返します。
注意
クォートは不要なので、magic_quotes_gpc や addslashes() を oci_bind_by_name() と同時に使わないでください。 自動的に追加されたクォートはデータベースの中にそのまま書き込まれてしまいます。 oci_bind_by_name() はデータをそのままの形で追加し、クォートやエスケープ文字の除去は行わないからです。
注意:
WHERE 句の中の CHAR 型のカラムに文字列をバインドするときには、 Oracle における CHAR 型のカラムの比較が スペースで埋めた (固定長形式で) 行われることを覚えておきましょう。 WHERE 句を期待通りに動作させるには、 PHP の変数側でもスペースを追加してカラムの幅と同じにしておく必要があります。
注意:
PHP の
variable引数は参照渡しです。 ループの形式によっては期待通りの動きをしないこともあります。<?php
foreach ($myarray as $key => $value) {
oci_bind_by_name($stid, $key, $value);
}
?>これは、それぞれのキーに対して $value の指す場所をバインドします。 つまり、すべてのバインド変数の値は ループの最後の処理で $value が指す値となります。 期待通りに動かすには、次のようにします。
<?php
foreach ($myarray as $key => $value) {
oci_bind_by_name($stid, $key, $myarray[$key]);
}
?>
注意:
PHP 5.0.0 より前のバージョンでは、かわりに ocibindbyname() を使用します。 現在のバージョンでは、古い関数名もまだ使えます。 しかし、これは廃止予定であり非推奨です。
参考
- oci_bind_array_by_name() - PHP の配列を Oracle PL/SQL の配列に名前でバインドする
- oci_parse() - 実行のために Oracle の文をパースする
//Calling Oracle Stored Procedure
//I assume that you have a users table and three columns in users table i.e. id, user, email in oracle
// For example I made connection in constructor, you can modify as per your requirement.
//http://www.devshed.com/c/a/PHP/Understanding-Destructors-in-PHP-5/1/
<?php
class Users{
private $connection;
public function __construct()
{
$this->connection = oci_connect("scott", "tiger", $db); // Establishes a connection to the Oracle server;
}
public function selectUsers($start_index=1, $numbers_of_rows=20)
{
$sql ="BEGIN sp_users_select(:p_start_index, :p_numbers_of_rows, :p_cursor, :p_result); END;";
$stmt = oci_parse($this->connection, $sql);
//Bind in parameter
oci_bind_by_name($stmt, ':p_start_index', $start_index, 20);
oci_bind_by_name($stmt, ':p_numbers_of_rows', $numbers_of_rows, 20);
//Bind out parameter
oci_bind_by_name($stmt, ':p_result', $result, 20); // returns 0 if stored procedure succeessfully executed.
//Bind Cursor
$p_cursor = oci_new_cursor($this->connection);
oci_bind_by_name($stmt, ':p_cursor', $p_cursor, -1, OCI_B_CURSOR);
// Execute Statement
oci_execute($stmt);
oci_execute($p_cursor, OCI_DEFAULT);
oci_fetch_all($p_cursor, $cursor, null, null, OCI_FETCHSTATEMENT_BY_ROW);
echo $result;
echo '<br>';
var_dump($cursor); // $cursor is an associative array so we can use print_r() to print this data.
// you can return data from this function to use it at your user interface.
}
public function deleteUser($id)
{
$sql ="BEGIN sp_user_delete(:p_id, :p_result); END;";
$stmt = oci_parse($this->connection, $sql);
// bind in and out variables
oci_bind_by_name($stmt, ':p_id', $id, 20);
oci_bind_by_name($stmt, ':p_result', $result, 20);
//Execute the statement
$check = oci_execute($stmt);
if($check == true)
$commit = oci_commit($this->connection);
else
$commit = oci_rollback($this->connection);
return $result;
}
// You can make function for insert ,update using above two functions
}
?>
If you get error "ORA-22275: invalid LOB locator specified ORA-06512" when using clob try this code:
<?php
$connection = $db->connection;
$statement = oci_parse($connection, 'begin core.clob_test(:p_varchar, :p_clob); end;');
$varchar = 'abc';
oci_bind_by_name($statement, ':p_varchar', $varchar);
$lob = oci_new_descriptor($connection, OCI_D_LOB);
oci_bind_by_name($statement, ':p_clob', $lob, -1, OCI_B_CLOB);
// Differences between original example is method name and methods call order
$lob->writeTemporary('abc abc abc abc abc');
oci_execute($statement);
$lob->close();
?>
I unfortunately spent the whole day trying to make this work as part of OCI bind_by_name insert:
<?php
if(is_numeric($v2)){
oci_bind_by_name($stmth, $bvar, $v2, -1, OCI_B_INT);
}else{
$v2 = (string) $v2;
oci_bind_by_name($stmth, $bvar, $v2, -1, SQLT_CHR);
}
?>
The string field is always inserting correctly w/o any truncation. The string field is a varchar2(160) CHAR, but the data used to populate it is 40 chars in length.
The numeric part is of Type Number in the database which is being used to store unix time (10 digit seconds since 1970/01/01.
The problem, the insert was truncating to 9 digits with some bogus value not even related to the input i.e., it's not just a matter of dropping the leftmost or rightmost digit, it'll just insert a 9 digit bogus number.
The only way I was able to resolve this for the numeric field was to set the maxlength to 8 (not 10 which is the number of digits in the input):
<?php
if(is_numeric($v2)){
oci_bind_by_name($stmth, $bvar, $v2, 8, OCI_B_INT);
}else{
$v2 = (string) $v2;
oci_bind_by_name($stmth, $bvar, $v2, -1, SQLT_CHR);
}
?>
Hopefully you'll see this soon before you expend a lot of time repeating the same problem I had.
Dont forget the 5th parameter: $type. It's will slowly your code some times. Eg:
<?php
$sql = "select * from (select * from b xxx) where rownum < :rnum";
$stmt = OCIParse($conn,$sql);
OCIBindByName($stmt, ":rnum", $NUM, -1);
OCIExecute($stmt);
?>
Below code was slow 5~6 time than not use bind value.Change the 3rd line to:
<?php
OCIBindByName($stmt, ":rnum", $NUM, -1, SQLT_INT);
?>
will resloved this problem.
This issue is also in the ADODB DB class(adodb.sf.net), you will be careful for use the SelectLimit method.
This is what the old OCI_B_* constants are now called:
(PHP 5.1.6 win32)
OCI_B_NTY - SQLT_NTY
OCI_B_BFILE - SQLT_BFILEE
OCI_B_CFILEE - SQLT_CFILEE
OCI_B_CLOB - SQLT_CLOB
OCI_B_BLOB - SQLT_BLOB
OCI_B_ROWID - SQLT_RDD
OCI_B_CURSOR - SQLT_RSET
OCI_B_BIN - SQLT_BIN
OCI_B_INT - SQLT_INT
OCI_B_NUM - SQLT_NUM
This is an example of returning the primary key from an insert so that you can do inserts on other tables with foreign keys based on that value. The date is just used to provied semi-unique data to be inserted.
$conn = oci_connect("username", "password")
$stmt = oci_parse($conn, "INSERT INTO test (test_msg) values (:data) RETURN test_id INTO :RV");
$data = date("d-M-Y H:i:s");
oci_bind_by_name($stmt, ":RV", $rv, -1, SQLT_INT);
oci_bind_by_name($stmt, ":data", $data, 24);
oci_execute($stmt);
print $rv;
Note that there have been some changes on the constant identifiers and the documentation is currently not entirely accurate.
Running the following script;
<?php
foreach (array_keys(get_defined_constants()) as $const) {
if ( preg_match('/^OCI_B_/', $const) ) {
print "$const\n";
}
}
?>
Under PHP 4.4.0 I get;
OCI_B_SQLT_NTY < renamed to OCI_B_NTY with PHP5
OCI_B_BFILE
OCI_B_CFILEE
OCI_B_CLOB
OCI_B_BLOB
OCI_B_ROWID
OCI_B_CURSOR
OCI_B_BIN
Under PHP 5.0.4 I get;
OCI_B_NTY
OCI_B_BFILE < docs are wrong right now
OCI_B_CFILEE < docs are wrong right now
OCI_B_CLOB
OCI_B_BLOB
OCI_B_ROWID
OCI_B_CURSOR
OCI_B_BIN < it's a mystery
I unfortunately used the code below for a data synchronize process and ended up in big trouble:
<?php
/*
cache data from other source
*/
$u=oci_parse($con,"UPDATE....WHERE id=:b_id");
oci_bind_by_name($u,":b_id",$id,-1,SQLT_INT);
//bind value...
$i=oci_parse($con,"INSERT INTO...");
oci_bind_by_name($i,":b_id",$id,-1,SQLT_INT);
//bind value using the same variables
while(/*read data from cache*/)
{
oci_execute($u,OCI_NO_AUTO_COMMIT);
if(oci_num_rows($u)<=0) oci_execute($i,OCI_NO_AUTO_COMMIT);
}
oci_commit($con);
?>
Although in fact the $i statement is never executed, after commit, I found that ALL data are copied as the last row of cached source. I then spent the next hour rebuilding the whole table.
So it seems it's not OK to bind the same variables to two (or more) statements, even if just one statement is actually executed.
I guess this is not a good practice to even think of using more than one statement each time, as no one has said it before, but I think this doesn't clearly violate the "be careful for address binding" rule, so it's still worth to be noted here to prevent some headache.
