【发布时间】:2011-07-10 23:06:12
【问题描述】:
我使用 Oracle 后端(OCI8 函数)维护一个 PHP 驱动的应用程序。该应用使用 Oracle 10g XE 开发并部署在客户拥有的任何版本上。
该应用程序处理单字节文本 (ISO-8859-15),在针对 Oracle XE 的西欧 版进行开发时,我从未遇到任何问题。但是,我最近安装了 Universal 版本,在插入带有非 ASCII 字符的大字符串时遇到了问题。此版本设置NLS_CHARACTERSET = AL32UTF8;因为我的应用程序使用WE8ISO8859P15 Oracle 默默地将我的输入数据从 ISO-8859-15 转换为 UTF-8(这很好)。但似乎某些大小检查出错了:包含 1500 个€ 字符的字符串(ISO-8889-15 中为 1500 字节,UTF-8 中为 4500 字节)似乎溢出了VARCHAR2(4000 CHAR) 列。
我已经创建了这个测试表:
CREATE TABLE FOO (
FOO_ID NUMBER NOT NULL ENABLE,
DATA_BYTE VARCHAR2(4000 BYTE),
DATA_CHAR VARCHAR2(4000 CHAR),
CONSTRAINT FOO_PK PRIMARY KEY (FOO_ID)
);
这个问题可以用这段代码重现:
<?php
$connection = oci_connect(DB_USER, DB_PASS, DB_CONN_STRING, 'WE8ISO8859P15');
if( !$connection ){
$e = oci_error();
die(htmlspecialchars($e['message']));
}
$id = 1;
$data = str_repeat('€', 1500);
$sql = 'INSERT INTO FOO (FOO_ID, DATA_CHAR) ' .
'VALUES (:id, :data)';
$res = oci_parse($connection, $sql);
if(!$res){
$e = oci_error();
die(htmlspecialchars($e['message']));
}
if(!oci_bind_by_name($res, ':id', $id)){
$e = oci_error();
die(htmlspecialchars($e['message']));
}
if(!oci_bind_by_name($res, ':data', $data)){
$e = oci_error();
die(htmlspecialchars($e['message']));
}
if(!oci_execute($res, OCI_COMMIT_ON_SUCCESS)){
$e = oci_error();
die(htmlspecialchars($e['message']));
}
...触发:
警告:oci_execute(): ORA-01461: sólo puede enlazar un valor LONG para insertarlo en una columna 长
这与我尝试插入 4001 字符字符串时遇到的错误相同。如果我插入 xxx... 而不是 €€€ 则不会发生这种情况 如果我将脚本保存为 UTF-8 并像这样连接也不会发生这种情况:
<?php
$connection = oci_connect(DB_USER, DB_PASS, DB_CONN_STRING, 'AL32UTF8');
[更新:我的测试有缺陷。使用 UTF-8 并不能避免 ORA-01461]
如何解决这个问题? NLS_CHARACTERSET 数据库参数不是我控制的,将我的应用程序切换到 UTF-8 可能会导致其他问题(几乎我们所有的客户都有单字节数据库)。
【问题讨论】: