I've gotten to a point where I absolutely need some clean way to safely pass lists/arrays from php to SQL server stored procedures and table value functions. The PHP SQL server driver still does not support table valued parameters, according to Microsoft docs.
In another question, using XML was suggested as an alternative.
Does anyone have a code sample for passing the equivalent of a TVP using an XML stream and PHP PDO or another clean alternative?
The primitive, but foolproof solution, is to pass it as a delimited string, and use a SPLIT function in your proc to convert the string to a table that you can then JOIN to.
Google SQL SPLIT FUNCTION to get free cut-n-paste code.
You could use xml, but a better, more compact idea would be to use JSON.
$myArray = array("thing 1", "thing 2", "thing 3");
$sql = $pdo->prepare("INSERT INTO `table` (`array_data`) VALUES (:myArray)");
$sql->execute(array(":myArray"=>json_encode($myArray)));
Then when you pull the data back out of the database you can convert it back into an array with:
$myArray = json_decode($res['myArray'], true);
Related
I stored an image in a PostgreSQL database with column type bytea using PHP. The problem is every time I try to load the image in a browser it does not appear. The Firefox developer console says the image is either truncated or corrupt.
The PHP code:
//code for inserting into the database
if(array_key_exists('submit_pic', $_POST)){
$user=$_SESSION['name'];
if(isset($_FILES['thumbnail'])&&$_FILES['thumbnail']['size']>0){
$fi = $_FILES['thumbnail']['tmp_name'];
$p=fopen($fi,'r');
$data=fread($p,filesize($fi));
$data=addslashes($data);
$dat= pg_escape_bytea($data);
$q="update userinfo set image='{$dat}' where email='$user'";
$e=pg_query($q)or die(pg_last_error());
// code for retreving from database
require_once('conn.php');
session_start();
$user=$_SESSION['name'];
pg_query('SET bytea_output = "escape";');
$lquery ="select image from userinfo where email='$user'";
$lq = pg_query($lquery)or die(pg_last_error());
$lqq=pg_fetch_row($lq,'image');
header("conent-type:image");
echo pg_unescape_bytea($lqq[0]);
and i need to store the uploaded image in a database- i am actually using heroku thanks
TL;DR:
Delete addslashes($data). It's redundant here.
Double-escaping .. twice
$data=fread($p,filesize($fi));
$data=addslashes($data);
$dat= pg_escape_bytea($data);
You read the data in, escape it as if it were a string literal, then convert it to bytea octal or hex escapes. It could never work that way around even if pg_escape_bytea was sane, which it isn't.
PHP's pg_escape_bytea appears to double-escape the output so it can be inserted into a string literal. This is incredibly ugly, but there doesn't appear to be an alternative that doesn't do this double-escaping, so you can't seem to use parameterised statements for bytea in PHP. You should still do so for everything else.
In this case, simply removing the addslashes line for the data read in from the file is sufficient.
Test case showing that pg_escape_bytea double-escapes (and always uses the old, inefficient octal escapes, too):
<?php
# oh-the-horror.php
print pg_escape_bytea("Blah binary\x00\x01\x02\x03\x04 blah");
?>
Run:
php oh-the-horror.php
Result:
Blah binary\\000\\001\\002\\003\\004 blah
See the doubled backslashes? That's because it's assuming you're going to interpolate it into SQL as a string, which is extremely memory inefficient, ugly, and a very bad habit. You don't seem to get any alternative, though.
Among other things this means that:
pg_unescape_bytea(pg_escape_bytea("\x01\x02\x03"));
... produces the wrong result, since pg_unescape_bytea is not actually the reverse of pg_escape_bytea. It also makes it impossible to feed the output of pg_escape_bytea into pg_query_params as a parameter, you have to interpolate it in.
Decoding
If you're using a modern PostgreSQL, it probably sets bytea_output to hex by default. That means that if I write my data to a bytea field then fetch it back, it'll look something like this:
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)
"Um, what", you might say? It's fine, it's just PostgreSQL's slightly more compact hex representation of bytea. pg_unescape_bytea will handle it fine and produce the same raw bytes as output ... if you have a modern PHP and libpq. On older versions you'll get garbage and will need to set bytea_output to escape for pg_unescape_bytea to handle it.
What you should do instead
Use PDO.
It has sane(ish) support for 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();
See:
PHP: Large Objects, which has an example of exactly what you want;
PDOStatement::bindParam
how to store serialized object with namespace in database using pdo php
Bind BYTEA to PGSQL PDO Prepared Statement in PHP5
You may also want to look in to PostgreSQL's lob (large object) support, which provides a streaming, seekable interface that's still fully transactional.
Now, on to my soap box
If PHP had a real distinction between "byte string" and "text string" types, you wouldn't even need pg_escape_bytea as the database driver could do it for you. None of this ugliness would be required. Unfortunately, there are no separate string and bytes types in PHP.
Please, use PDO with parameterised statements as much as possible.
Where you can't, at least use pg_query_params and parameterised statements. PHP's addslashes is not an alternative, it's inefficient, ugly, and doesn't understand database specific escaping rules. You still have to manually escape bytea if you're not using PDO for icky historical reasons, but everything else should go through parameterised statements.
For guidance on pg_query_params:
Bobby tables, PHP section.
The PHP manual on pg_query_params
It is better to use postgres large objects if you really have to store images in your database. In the userinfo table instead of the image itself store just a link to it as loid (large object id).
Insert an image into the database:
pg_query("begin"); // pg_lo functions need to be run in a transaction
$loid = pg_lo_import('full_path_and_file_name');
pg_query("update userinfo set loid=$loid where email='$user'");
pg_query("commit");
Retrieve an image from the database:
$rs = pg_query("select loid from userinfo where email='$user'");
$loid = pg_fetch_row($rs, 0)[0];
pg_query("begin");
$blob = pg_lo_open($loid, "r");
header("Content-type: image");
pg_lo_read_all($blob);
pg_lo_close($blob);
pg_query("commit");
The loid field is of type oid (of course you can name it as you want).
Consider using the lo type from the lo extension instead of using the oid type. Using lo gives you automatic "orphan removal", where deleting a row from a table will automatically remove the associated large object, so it's good for cases where a table row "owns" a large object.
Storing links to images is especially convenient in case you use one image more than one time. However, you should pay attention to delete unused images from your database (PHP function pg_lo_unlink()).
Large objects in postgres documentation.
PHP manual: pg_lo_import.
I found a strange way of getting this to work too without using PDO.
Use a text field in postgresql instead of bytea.
On insert, prep your data like this:
$imgdta = pg_escape_string(bin2hex($filedata));
Then when you want to display the file after your query, use:
echo pack("H*", $img["filedata"]);
I'm not going to pretend I get why, but this worked for me!
As the source of your data is a file in the file system so it seems to me efficient to find an inspiration here:
In your db create an auxiliary function, run as superuser:
create or replace function bytea_import(p_path text, p_result out bytea)
language plpgsql as $$
declare
l_oid oid;
begin
select lo_import(p_path) into l_oid;
select lo_get(l_oid) INTO p_result;
perform lo_unlink(l_oid);
end;$$
security definer;
In your php execute a query like:
#make sure that postgres will have access to the file
chmod($_FILES['thumbnail']['tmp_name'], 0644);
pg_query("update userinfo set image=(select bytea_import('".$_FILES['thumbnail']['tmp_name']."')) where email='$user'");
I'm new to PHP, but not programming. Have come from an ASP [classic] background. In brief, I'm using PHP 5.4, with FastCGI on IIS7 and SQL Server 2005 Express. I've learnt the fundamentals, and have spent quite some time looking into security.
I'm sanitising both GET and POST input data. My db connection strings are in a separate file placed outside the web root. I'm using PDO prepared statements [though I've heard query+quote perform faster] with named placeholders along with db stored procedures.
I'm trying to understand why I would need to use additional arguments within the bindParam function, particularly data type options "PDO::PARAM_STR, 12" [second argument in that example represent the data length right?].
What are the benefits of specifying the data type and length within the bindParam? Is it needed if I'm using stored procedures in which the data type and length is already specified? Also, I believe I need to use something like "PDO::PARAM_INPUT_OUTPUT" to return a value from a stored proc?
Thanks!
** EDIT **
For some reason, if I use the PDO::PARAM_STR argument, my stored procs don't seem to write data into the db. So I omitted that argument. Here's my code:
$sql1 = $conn->prepare("EXEC insert_platts :userAgent, :userIp, 1, :source");
$sql1->bindParam(':userAgent', $userAgent);
$sql1->bindParam(':userIp', $userIp);
$sql1->bindParam(':source', $source);
$sql1->execute();
Also, rather than returning the identity value from the stored proc, I'm using lastInsertId() instead:
$lastRow = $conn->lastInsertId();
print $lastRow;
No, data type and data length are not needed. I'm using mysql stored procs and the parameters are never typed values, all though I validate them of course. I guess that the reason is extra security and INOUT params. Quote:
To return an INOUT parameter from a stored procedure, use the bitwise
OR operator to set the PDO::PARAM_INPUT_OUTPUT
have you tried this?
$params = array(
':userAgent'=>$userAgent,
':userIp' => $userIp,
':source' => $source
);
$sql1 = $conn->prepare("EXEC insert_platts :userAgent, :userIp, 1, :source");
$sql1->execute($params);
About special characters: are you using correct encodings? I mean, the same encoding in the php app and the DB... sometimes is hard to work with one encoding in the scripts and other in the database.. and very often problems like that arise...
This question already has answers here:
Getting raw SQL query string from PDO prepared statements
(16 answers)
Closed 6 years ago.
In PHP, when accessing MySQL database with PDO with parametrized query, how can you check the final query (after having replaced all tokens)?
Is there a way to check what gets really executed by the database?
So I think I'll finally answer my own question in order to have a full solution for the record. But have to thank Ben James and Kailash Badu which provided the clues for this.
Short Answer
As mentioned by Ben James: NO.
The full SQL query does not exist on the PHP side, because the query-with-tokens and the parameters are sent separately to the database.
Only on the database side the full query exists.
Even trying to create a function to replace tokens on the PHP side would not guarantee the replacement process is the same as the SQL one (tricky stuff like token-type, bindValue vs bindParam, ...)
Workaround
This is where I elaborate on Kailash Badu's answer.
By logging all SQL queries, we can see what is really run on the server.
With mySQL, this can be done by updating the my.cnf (or my.ini in my case with Wamp server), and adding a line like:
log=[REPLACE_BY_PATH]/[REPLACE_BY_FILE_NAME]
Just do not run this in production!!!
You might be able to use PDOStatement->debugDumpParams. See the PHP documentation .
Using prepared statements with parametrised values is not simply another way to dynamically create a string of SQL. You create a prepared statement at the database, and then send the parameter values alone.
So what is probably sent to the database will be a PREPARE ..., then SET ... and finally EXECUTE ....
You won't be able to get some SQL string like SELECT * FROM ..., even if it would produce equivalent results, because no such query was ever actually sent to the database.
I check Query Log to see the exact query that was executed as prepared statement.
I initially avoided turning on logging to monitor PDO because I thought that it would be a hassle but it is not hard at all. You don't need to reboot MySQL (after 5.1.9):
Execute this SQL in phpMyAdmin or any other environment where you may have high db privileges:
SET GLOBAL general_log = 'ON';
In a terminal, tail your log file. Mine was here:
>sudo tail -f /usr/local/mysql/data/myMacComputerName.log
You can search for your mysql files with this terminal command:
>ps auxww|grep [m]ysqld
I found that PDO escapes everything, so you can't write
$dynamicField = 'userName';
$sql = "SELECT * FROM `example` WHERE `:field` = :value";
$this->statement = $this->db->prepare($sql);
$this->statement->bindValue(':field', $dynamicField);
$this->statement->bindValue(':value', 'mick');
$this->statement->execute();
Because it creates:
SELECT * FROM `example` WHERE `'userName'` = 'mick' ;
Which did not create an error, just an empty result. Instead I needed to use
$sql = "SELECT * FROM `example` WHERE `$dynamicField` = :value";
to get
SELECT * FROM `example` WHERE `userName` = 'mick' ;
When you are done execute:
SET GLOBAL general_log = 'OFF';
or else your logs will get huge.
What I did to print that actual query is a bit complicated but it works :)
In method that assigns variables to my statement I have another variable that looks a bit like this:
$this->fullStmt = str_replace($column, '\'' . str_replace('\'', '\\\'', $param) . '\'', $this->fullStmt);
Where:
$column is my token
$param is the actual value being assigned to token
$this->fullStmt is my print only statement with replaced tokens
What it does is a simply replace tokens with values when the real PDO assignment happens.
I hope I did not confuse you and at least pointed you in right direction.
The easiest way it can be done is by reading mysql execution log file and you can do that in runtime.
There is a nice explanation here:
How to show the last queries executed on MySQL?
I don't believe you can, though I hope that someone will prove me wrong.
I know you can print the query and its toString method will show you the sql without the replacements. That can be handy if you're building complex query strings, but it doesn't give you the full query with values.
I think easiest way to see final query text when you use pdo is to make special error and look error message. I don't know how to do that, but when i make sql error in yii framework that use pdo i could see query text
When using mysql_fetch_object() to return objects from a MySQL query, sometimes column names have spaces in them, and cannot be aliased, such as when running SHOW CREATE PROCEDURE. The procedure definition is returned in a column named Create Procedure. In my case, the data abstraction layer only allows the use of mysql_fetch_object(), so I can't simply use mysql_fetch_assoc() to work around this problem.
Can I access columns with spaces in when using mysql_fetch_object()?
Generally speaking,
$recordname->{"my column name"}
will do the trick.
You can also do a print_r($record); to find out how the columns are represented in the object.
This took me a while to crack, so I thought I'd post it here, hence the quick answer. :)
// Internally, this db abstraction layer uses mysql_fetch_object().
$query = $db->query('SHOW CREATE PROCEDURE `%s`', 'test');
foreach ($query as $row) {
$procedure = $row->{'Create Procedure'};
}
I don't know if this is specific to a PHP version, but it works under PHP 5.3.1 on Windows, at least.
Use quotes. Example: "name with spaces"
This question already has answers here:
Getting raw SQL query string from PDO prepared statements
(16 answers)
Closed 6 years ago.
In PHP, when accessing MySQL database with PDO with parametrized query, how can you check the final query (after having replaced all tokens)?
Is there a way to check what gets really executed by the database?
So I think I'll finally answer my own question in order to have a full solution for the record. But have to thank Ben James and Kailash Badu which provided the clues for this.
Short Answer
As mentioned by Ben James: NO.
The full SQL query does not exist on the PHP side, because the query-with-tokens and the parameters are sent separately to the database.
Only on the database side the full query exists.
Even trying to create a function to replace tokens on the PHP side would not guarantee the replacement process is the same as the SQL one (tricky stuff like token-type, bindValue vs bindParam, ...)
Workaround
This is where I elaborate on Kailash Badu's answer.
By logging all SQL queries, we can see what is really run on the server.
With mySQL, this can be done by updating the my.cnf (or my.ini in my case with Wamp server), and adding a line like:
log=[REPLACE_BY_PATH]/[REPLACE_BY_FILE_NAME]
Just do not run this in production!!!
You might be able to use PDOStatement->debugDumpParams. See the PHP documentation .
Using prepared statements with parametrised values is not simply another way to dynamically create a string of SQL. You create a prepared statement at the database, and then send the parameter values alone.
So what is probably sent to the database will be a PREPARE ..., then SET ... and finally EXECUTE ....
You won't be able to get some SQL string like SELECT * FROM ..., even if it would produce equivalent results, because no such query was ever actually sent to the database.
I check Query Log to see the exact query that was executed as prepared statement.
I initially avoided turning on logging to monitor PDO because I thought that it would be a hassle but it is not hard at all. You don't need to reboot MySQL (after 5.1.9):
Execute this SQL in phpMyAdmin or any other environment where you may have high db privileges:
SET GLOBAL general_log = 'ON';
In a terminal, tail your log file. Mine was here:
>sudo tail -f /usr/local/mysql/data/myMacComputerName.log
You can search for your mysql files with this terminal command:
>ps auxww|grep [m]ysqld
I found that PDO escapes everything, so you can't write
$dynamicField = 'userName';
$sql = "SELECT * FROM `example` WHERE `:field` = :value";
$this->statement = $this->db->prepare($sql);
$this->statement->bindValue(':field', $dynamicField);
$this->statement->bindValue(':value', 'mick');
$this->statement->execute();
Because it creates:
SELECT * FROM `example` WHERE `'userName'` = 'mick' ;
Which did not create an error, just an empty result. Instead I needed to use
$sql = "SELECT * FROM `example` WHERE `$dynamicField` = :value";
to get
SELECT * FROM `example` WHERE `userName` = 'mick' ;
When you are done execute:
SET GLOBAL general_log = 'OFF';
or else your logs will get huge.
What I did to print that actual query is a bit complicated but it works :)
In method that assigns variables to my statement I have another variable that looks a bit like this:
$this->fullStmt = str_replace($column, '\'' . str_replace('\'', '\\\'', $param) . '\'', $this->fullStmt);
Where:
$column is my token
$param is the actual value being assigned to token
$this->fullStmt is my print only statement with replaced tokens
What it does is a simply replace tokens with values when the real PDO assignment happens.
I hope I did not confuse you and at least pointed you in right direction.
The easiest way it can be done is by reading mysql execution log file and you can do that in runtime.
There is a nice explanation here:
How to show the last queries executed on MySQL?
I don't believe you can, though I hope that someone will prove me wrong.
I know you can print the query and its toString method will show you the sql without the replacements. That can be handy if you're building complex query strings, but it doesn't give you the full query with values.
I think easiest way to see final query text when you use pdo is to make special error and look error message. I don't know how to do that, but when i make sql error in yii framework that use pdo i could see query text