A big gotcha exists for Oracle users.
You have to save CLOB objects using PDO::PARAM_STR, not PDO::PARAM_LOB.
But you MUST send the 4th argument, usually strlen($subject) or you get a LONG error.
En algún momento, una aplicación podría necesitar almacenar
datos "grandes" en la base de datos. Grande típicamente significa "alrededor de 4kb o
más", aunque algunas bases de datos pueden manejar fácilmente hasta 32kb antes de que
los datos se consideren "grandes". Los objetos grandes, o LOB (acrónimo en inglés de 'Large OBjects'), pueden ser de texto o binarios.
PDO permite trabajar con este tipo de datos grandes utilizando el código del tipo
PDO::PARAM_LOB
en llamadas a PDOStatement::bindParam() o
PDOStatement::bindColumn().
PDO::PARAM_LOB
indica a
PDO que haga corresponder los datos como un flujo, pudiendo manipularlos así utilizando la
API de flujos de PHP.
Ejemplo #1 Mostrar una imagen desde una base de datos
Este ejemplo vincula un LOB a una variable llamada $lob, y luego lo envía al navegador usando fpassthru(). Ya que el LOB está representado como un flujo, se pueden emplear funciones tales como fgets(), fread() y stream_get_contents() para manejarlo.
<?php
$bd = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
$sentencia = $bd->prepare("select contenttype, imagedata from images where id=?");
$sentencia->execute(array($_GET['id']));
$sentencia->bindColumn(1, $tipo, PDO::PARAM_STR, 256);
$sentencia->bindColumn(2, $lob, PDO::PARAM_LOB);
$sentencia->fetch(PDO::FETCH_BOUND);
header("Content-Type: $tipo");
fpassthru($lob);
?>
Ejemplo #2 Insertar una imagen en una base de datos
Este ejemplo abre un fichero y pasa el manejador del fichero a PDO para insertarlo como un LOB. PDO hará todo lo posible para enviar el contenido del fichero a la base de datos de la manera más eficiente.
<?php
$bd = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
$sentencia = $bd->prepare("insert into images (id, contenttype, imagedata) values (?, ?, ?)");
$id = get_new_id(); // alguna función para asignar un nuevo ID
// Se asume que se está ejecutando como parte de un formulario de subida de ficheros
// Se puede encontrar más información en la documentación de PHP
$fp = fopen($_FILES['file']['tmp_name'], 'rb');
$sentencia->bindParam(1, $id);
$sentencia->bindParam(2, $_FILES['file']['type']);
$sentencia->bindParam(3, $fp, PDO::PARAM_LOB);
$bd->beginTransaction();
$sentencia->execute();
$bd->commit();
?>
Ejemplo #3 Insertar una imagen en una base de datos: Oracle
Oracle requiere una sintaxis ligeramente diferente para insertar un LOB desde un fichero. También es esencial que se realice la inserción bajo una transacción, si no, el LOB recién insertado será consignado con una longitud cero como parte de la consigna implícita que ocurre cuando la consulta se ejecuta:
<?php
$bd = new PDO('oci:', 'scott', 'tiger');
$sentencia = $bd->prepare("insert into images (id, contenttype, imagedata) " .
"VALUES (?, ?, EMPTY_BLOB()) RETURNING imagedata INTO ?");
$id = get_new_id(); // alguna función para asignar un nuevo ID
// Se asume que se está ejecutando como parte de un formulario de subida de ficheros
// Se puede encontrar más información en la documentación de PHP
$fp = fopen($_FILES['file']['tmp_name'], 'rb');
$sentencia->bindParam(1, $id);
$sentencia->bindParam(2, $_FILES['file']['type']);
$sentencia->bindParam(3, $fp, PDO::PARAM_LOB);
$bd->beginTransaction();
$sentencia->execute();
$bd->commit();
?>
A big gotcha exists for Oracle users.
You have to save CLOB objects using PDO::PARAM_STR, not PDO::PARAM_LOB.
But you MUST send the 4th argument, usually strlen($subject) or you get a LONG error.
There seems to be a bug that affects example 1 above. PDO::PARAM_LOB when used with pdo::bindColumn() is supposed to return a stream but it returns a string. Passing this string to fpassthru() then triggers an error with the message 'supplied argument is not a valid stream resource'. This has been reported in bug #40913. The work around is to do the following:
<?php
$stmt = $db->prepare("select contenttype, imagedata from images where id=?");
$stmt->execute(array($_GET['id']));
$stmt->bindColumn(1, $type, PDO::PARAM_STR, 256);
$stmt->bindColumn(2, $lob, PDO::PARAM_LOB);
$stmt->fetch(PDO::FETCH_BOUND);
header("Content-Type: $type");
echo($lob);
?>
Since the browser is expecting an image after the call to header() writing the string representation of the binary output with echo() has the same affect as calling fpassthru().
PDOStatement's methods bindParam and bindValue also work with strings, as in:
<?php
$data = file_get_contents($filename);
$stmt->bindValue(1, $data, PDO::PARAM_LOB);
//...
?>
This was the only way I could make it work with PostgreSQL.
I spend a lot of time trying to get this to work, but no matter what I did PDO corrupted my data.
I finally discovered that I had been using:
$pdo->exec('SET CHARACTER SET utf8');
in the TRY part of my connection script.
This off course doesn't work when you feed binary input to PDO using the parameter lob.
For selecting data out of Postgres, the data type of the column in the table determined if the parameter bound with PARAM_LOB returned a string or returned a resource.
<?php
// create table log ( data text ) ;
$geth = $dbh->prepare('select data from log ');
$geth->execute();
$geth->bindColumn(1, $dataString, PDO::PARAM_LOB);
$geth->fetch(PDO::FETCH_BOUND);
echo ($dataString); // $dataString is a string
// create table log ( data bytea ) ;
$geth = $dbh->prepare('select data from log');
$geth->execute();
$geth->bindColumn(1, $dataFH, PDO::PARAM_LOB);
$geth->fetch(PDO::FETCH_BOUND);
fpassthru($dataFH); // $dataFH is a resource
The DBMSs that are listed above have these (default) limits on the maximum size of a char string. The maximum is given in bytes so the number of characters storable can be smaller if a multibyte encoding is used.
CUBRID: 16kB
SQL Server: 2GB
Firebird: 32kB
IBM Db2: 32kB
Informix: 32kB
MySQL: 16kB
Oracle: 2kB
PostgreSQL: 1GB
SQLite: 1 billion bytes
4D: Unknown, but LOBs are limited to 2GB.