PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Stocker des images dans des champs bytea dans une base de données PostgreSQL

TL; DR :

Supprimer addslashes($data) . C'est redondant ici.

Double échappement .. deux fois

$data=fread($p,filesize($fi));
$data=addslashes($data);
$dat= pg_escape_bytea($data); 

Vous lisez les données, les échappez comme s'il s'agissait d'un littéral de chaîne, puis les convertissez en octets d'échappement octal ou hexadécimal. Cela ne pourrait jamais fonctionner de cette façon même si pg_escape_bytea était sain d'esprit, ce qui n'est pas le cas.

pg_escape_bytea de PHP semble double-échappement la sortie afin qu'elle puisse être insérée dans un littéral de chaîne. C'est incroyablement moche, mais il ne semble pas y avoir d'alternative qui ne fasse pas ce double échappement, donc vous ne semblez pas pouvoir utiliser des instructions paramétrées pour bytea en PHP. Vous devriez toujours le faire pour tout le reste.

Dans ce cas, en supprimant simplement les addslashes ligne pour les données lues à partir du fichier est suffisante.

Cas de test montrant que pg_escape_bytea doubles échappements (et utilise toujours les anciens échappements octaux inefficaces) :

<?php
# oh-the-horror.php
print pg_escape_bytea("Blah binary\x00\x01\x02\x03\x04 blah");
?>

Exécuter :

php oh-the-horror.php

Résultat :

Blah binary\\000\\001\\002\\003\\004 blah

Vous voyez les doubles barres obliques inverses ? C'est parce qu'il suppose que vous allez l'interpoler dans SQL sous forme de chaîne, ce qui est extrêmement inefficace en termes de mémoire, laid et une très mauvaise habitude. Cependant, vous ne semblez pas avoir d'alternative.

Cela signifie entre autres que :

pg_unescape_bytea(pg_escape_bytea("\x01\x02\x03"));

... produit le résultat erroné , depuis pg_unescape_bytea n'est pas en fait l'inverse de pg_escape_bytea . Cela rend également impossible d'alimenter la sortie de pg_escape_bytea dans pg_query_params en tant que paramètre, vous devez l'interpoler.

Décodage

Si vous utilisez un PostgreSQL moderne, il définit probablement bytea_output en hex par défaut. Cela signifie que si j'écris mes données dans un bytea puis récupérez-le, il ressemblera à ceci :

craig=> CREATE TABLE byteademo(x bytea);
CREATE TABLE
craig=> INSERT INTO byteademo(x) VALUES ('Blah binary\\000\\001\\002\\003\\004 blah');
INSERT 0 1
craig=> SELECT * FROM byteademo ;
                                     x                                      
----------------------------------------------------------------------------
 \x426c61682062696e6172795c3030305c3030315c3030325c3030335c30303420626c6168
(1 row)

« Euh, quoi », pourriez-vous dire ? C'est bien, c'est juste la représentation hexadécimale légèrement plus compacte de bytea de PostgreSQL . pg_unescape_bytea le gérera bien et produira les mêmes octets bruts que la sortie ... si vous avez un PHP moderne et libpq . Sur les anciennes versions, vous obtiendrez des ordures et devrez définir bytea_output pour escape pour pg_unescape_bytea pour le gérer.

Ce que vous devriez faire à la place

Utilisez AOP.

Il a un support sain (plutôt) pour bytea .

$sth = $pdo->prepare('INSERT INTO mytable(somecol, byteacol) VALUES (:somecol, :byteacol)');
$sth->bindParam(':somecol', 'bork bork bork');
$sth->bindParam(':byteacol', $thebytes, PDO::PARAM_LOB);
$sth->execute();

Voir :

  • PHP :Large Objects, qui contient un exemple de ce que vous voulez exactement ;
  • PDOStatement::bindParam
  • comment stocker un objet sérialisé avec un espace de noms dans la base de données à l'aide de pdo php
  • Lier BYTEA à l'instruction préparée PGSQL PDO en PHP5

Vous pouvez également consulter la prise en charge des lob (objets volumineux) de PostgreSQL, qui fournit une interface de recherche en continu qui reste entièrement transactionnelle.

Maintenant, passons à ma boîte à savon

Si PHP avait une réelle distinction entre les types "chaîne d'octets" et "chaîne de texte", vous n'auriez même pas besoin de pg_escape_bytea car le pilote de base de données pourrait le faire pour vous. Aucune de ces laideurs ne serait nécessaire. Malheureusement, il n'y a pas de types de chaînes et d'octets distincts en PHP.

Veuillez utiliser PDO avec des instructions paramétrées autant que possible.

Là où vous ne pouvez pas, utilisez au moins pg_query_params et des instructions paramétrées. Les addslashes de PHP n'est pas une alternative, il est inefficace, moche et ne comprend pas les règles d'échappement spécifiques à la base de données. Vous devez toujours échapper manuellement bytea si vous n'utilisez pas PDO pour des raisons historiques délicates, mais tout le reste doit passer par des instructions paramétrées.

Pour obtenir des conseils sur pg_query_params :

  • Tableaux Bobby, section PHP.
  • Le manuel PHP sur pg_query_params